Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: d7a910be644c8509bad6f11e02b64a3505e69ce6 (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) 2005, 2010 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.baseadaptor;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import org.eclipse.osgi.baseadaptor.bundlefile.BundleEntry;
import org.eclipse.osgi.baseadaptor.bundlefile.BundleFile;
import org.eclipse.osgi.baseadaptor.hooks.ClassLoadingHook;
import org.eclipse.osgi.baseadaptor.hooks.StorageHook;
import org.eclipse.osgi.baseadaptor.loader.BaseClassLoader;
import org.eclipse.osgi.baseadaptor.loader.ClasspathManager;
import org.eclipse.osgi.framework.adaptor.*;
import org.eclipse.osgi.framework.debug.Debug;
import org.eclipse.osgi.framework.internal.core.*;
import org.eclipse.osgi.framework.internal.core.Constants;
import org.eclipse.osgi.framework.internal.protocol.bundleentry.Handler;
import org.eclipse.osgi.framework.log.FrameworkLogEntry;
import org.eclipse.osgi.internal.baseadaptor.ArrayMap;
import org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader;
import org.eclipse.osgi.util.ManifestElement;
import org.osgi.framework.*;

/**
 * The BundleData implementation used by the BaseAdaptor.
 * @see BaseAdaptor
 * @see BundleData
 * @see StorageHook
 * @see ClassLoadingHook
 * @since 3.2
 */
public class BaseData implements BundleData {
	private final static boolean COPY_NATIVES = Boolean.valueOf(FrameworkProperties.getProperty("osgi.classloader.copy.natives")).booleanValue(); //$NON-NLS-1$
	private long id;
	private BaseAdaptor adaptor;
	private Bundle bundle;
	private int startLevel = -1;
	private int status = 0;
	private StorageHook[] storageHooks;
	private String location;
	private long lastModified;
	protected BundleFile bundleFile;
	private ArrayMap<Object, BundleFile> bundleFiles;
	private boolean dirty = false;
	protected Dictionary<String, String> manifest;
	// This field is only used by PDE source lookup, and is set by a hook (bug 126517).  It serves no purpose at runtime.
	protected String fileName;
	// This is only used to keep track of when the same native library is loaded more than once
	protected Collection<String> loadedNativeCode;

	///////////////////// Begin values from Manifest     /////////////////////
	private String symbolicName;
	private Version version;
	private String activator;
	private String classpath;
	private String executionEnvironment;
	private String dynamicImports;
	private int type;

	///////////////////// End values from Manifest       /////////////////////

	/**
	 * Constructs a new BaseData with the specified id for the specified adaptor
	 * @param id the id of the BaseData
	 * @param adaptor the adaptor of the BaseData
	 */
	public BaseData(long id, BaseAdaptor adaptor) {
		this.id = id;
		this.adaptor = adaptor;
	}

	/**
	 * This method calls all the configured class loading hooks {@link ClassLoadingHook#createClassLoader(ClassLoader, ClassLoaderDelegate, BundleProtectionDomain, BaseData, String[])} 
	 * methods until on returns a non-null value.  If none of the class loading hooks returns a non-null value 
	 * then the default classloader implementation is used.
	 * @see BundleData#createClassLoader(ClassLoaderDelegate, BundleProtectionDomain, String[])
	 */
	public BundleClassLoader createClassLoader(ClassLoaderDelegate delegate, BundleProtectionDomain domain, String[] bundleclasspath) {
		ClassLoadingHook[] hooks = adaptor.getHookRegistry().getClassLoadingHooks();
		ClassLoader parent = adaptor.getBundleClassLoaderParent();
		BaseClassLoader cl = null;
		for (int i = 0; i < hooks.length && cl == null; i++)
			cl = hooks[i].createClassLoader(parent, delegate, domain, this, bundleclasspath);
		if (cl == null)
			cl = new DefaultClassLoader(parent, delegate, domain, this, bundleclasspath);
		return cl;
	}

	public final URL getEntry(final String path) {
		if (System.getSecurityManager() == null)
			return getEntry0(path);
		return AccessController.doPrivileged(new PrivilegedAction<URL>() {
			public URL run() {
				return getEntry0(path);
			}
		});
	}

	final URL getEntry0(String path) {
		BundleEntry entry = getBundleFile().getEntry(path);
		if (entry == null)
			return null;
		path = BundleFile.fixTrailingSlash(path, entry);
		try {
			//use the constant string for the protocol to prevent duplication
			return new URL(Constants.OSGI_ENTRY_URL_PROTOCOL, Long.toString(id) + BundleResourceHandler.BID_FWKID_SEPARATOR + Integer.toString(adaptor.hashCode()), 0, path, new Handler(entry, adaptor));
		} catch (MalformedURLException e) {
			return null;
		}
	}

	public final Enumeration<String> getEntryPaths(String path) {
		return getBundleFile().getEntryPaths(path);
	}

	/**
	 * This method calls each configured classloading hook {@link ClassLoadingHook#findLibrary(BaseData, String)} method 
	 * until the first one returns a non-null value.
	 * @see BundleData#findLibrary(String)
	 */
	public String findLibrary(String libname) {
		ClassLoadingHook[] hooks = adaptor.getHookRegistry().getClassLoadingHooks();
		String result = null;
		for (int i = 0; i < hooks.length; i++) {
			result = hooks[i].findLibrary(this, libname);
			if (result != null)
				break;
		}
		// check to see if this library has been loaded by another class loader
		if (result != null)
			synchronized (this) {
				if (loadedNativeCode == null)
					loadedNativeCode = new ArrayList<String>(1);
				if (loadedNativeCode.contains(result) || COPY_NATIVES) {
					// we must copy the library to a temp space to allow another class loader to load the library
					String temp = copyToTempLibrary(result);
					if (temp != null)
						result = temp;
				} else {
					loadedNativeCode.add(result);
				}
			}
		return result;
	}

	private String copyToTempLibrary(String result) {
		try {
			return adaptor.getStorage().copyToTempLibrary(this, result);
		} catch (IOException e) {
			adaptor.getFrameworkLog().log(new FrameworkLogEntry(FrameworkAdaptor.FRAMEWORK_SYMBOLICNAME, FrameworkLogEntry.ERROR, 0, e.getMessage(), 0, e, null));
		}
		return null;
	}

	public void installNativeCode(String[] nativepaths) throws BundleException {
		adaptor.getStorage().installNativeCode(this, nativepaths);
	}

	public File getDataFile(String path) {
		return adaptor.getStorage().getDataFile(this, path);
	}

	public Dictionary<String, String> getManifest() throws BundleException {
		if (manifest == null)
			manifest = adaptor.getStorage().loadManifest(this);
		return manifest;
	}

	public long getBundleID() {
		return id;
	}

	public final String getLocation() {
		return location;
	}

	/**
	 * Sets the location of this bundledata
	 * @param location the location of this bundledata
	 */
	public final void setLocation(String location) {
		this.location = location;
	}

	public final long getLastModified() {
		return lastModified;
	}

	/**
	 * Sets the last modified time stamp of this bundledata
	 * @param lastModified the last modified time stamp of this bundledata
	 */
	public final void setLastModified(long lastModified) {
		this.lastModified = lastModified;
	}

	public synchronized void close() throws IOException {
		if (bundleFile != null)
			getBundleFile().close(); // only close the bundleFile if it already exists.
		if (bundleFiles != null) {
			for (BundleFile bundlefile : bundleFiles.getValues())
				bundlefile.close();
			bundleFiles.clear();
		}
	}

	public void open() throws IOException {
		getBundleFile().open();
	}

	public final void setBundle(Bundle bundle) {
		this.bundle = bundle;
	}

	/**
	 * Returns the bundle object of this BaseData
	 * @return the bundle object of this BaseData
	 */
	public final Bundle getBundle() {
		return bundle;
	}

	public int getStartLevel() {
		return startLevel;
	}

	public int getStatus() {
		return status;
	}

	/**
	 * This method calls each configured storage hook {@link StorageHook#forgetStartLevelChange(int)} method.
	 * If one returns true then this bundledata is not marked dirty.
	 * @see BundleData#setStartLevel(int)
	 */
	public void setStartLevel(int value) {
		startLevel = setPersistentData(value, true, startLevel);
	}

	/**
	 * This method calls each configured storage hook {@link StorageHook#forgetStatusChange(int)} method.
	 * If one returns true then this bundledata is not marked dirty.
	 * @see BundleData#setStatus(int)
	 */
	public void setStatus(int value) {
		status = setPersistentData(value, false, status);
	}

	private int setPersistentData(int value, boolean isStartLevel, int orig) {
		StorageHook[] hooks = getStorageHooks();
		for (int i = 0; i < hooks.length; i++)
			if (isStartLevel) {
				if (hooks[i].forgetStartLevelChange(value))
					return value;
			} else {
				if (hooks[i].forgetStatusChange(value))
					return value;
			}
		if (value != orig)
			dirty = true;
		return value;
	}

	/**
	 * @throws IOException  
	 */
	public void save() throws IOException {
		adaptor.getStorage().save(this);
	}

	/**
	 * Returns true if this bundledata is dirty
	 * @return true if this bundledata is dirty
	 */
	public boolean isDirty() {
		return dirty;
	}

	/**
	 * Sets the dirty flag for this BaseData
	 * @param dirty the dirty flag
	 */
	public void setDirty(boolean dirty) {
		this.dirty = dirty;
	}

	public final String getSymbolicName() {
		return symbolicName;
	}

	/**
	 * Sets the symbolic name of this BaseData
	 * @param symbolicName the symbolic name
	 */
	public final void setSymbolicName(String symbolicName) {
		this.symbolicName = symbolicName;
	}

	public final Version getVersion() {
		return version;
	}

	/**
	 * Sets the version of this BaseData
	 * @param version the version
	 */
	public final void setVersion(Version version) {
		this.version = version;
	}

	public final int getType() {
		return type;
	}

	/**
	 * Sets the type of this BaseData
	 * @param type the type
	 */
	public final void setType(int type) {
		this.type = type;
	}

	public final String[] getClassPath() throws BundleException {
		ManifestElement[] classpathElements = ManifestElement.parseHeader(Constants.BUNDLE_CLASSPATH, classpath);
		return getClassPath(classpathElements);
	}

	// TODO make classpath a String[] instead of saving a comma separated string.
	public String getClassPathString() {
		return classpath;
	}

	//TODO make classpath a String[] instead of saving a comma separated string.
	public void setClassPathString(String classpath) {
		this.classpath = classpath;
	}

	public final String getActivator() {
		return activator;
	}

	/**
	 * Sets the activator of this BaseData
	 * @param activator the activator
	 */
	public final void setActivator(String activator) {
		this.activator = activator;
	}

	public final String getExecutionEnvironment() {
		return executionEnvironment;
	}

	/**
	 * Sets the execution environment of this BaseData
	 * @param executionEnvironment the execution environment
	 */
	public void setExecutionEnvironment(String executionEnvironment) {
		this.executionEnvironment = executionEnvironment;
	}

	public final String getDynamicImports() {
		return dynamicImports;
	}

	/**
	 * Sets the dynamic imports of this BaseData
	 * @param dynamicImports the dynamic imports
	 */
	public void setDynamicImports(String dynamicImports) {
		this.dynamicImports = dynamicImports;
	}

	/**
	 * Returns the adaptor for this BaseData
	 * @return the adaptor
	 */
	public final BaseAdaptor getAdaptor() {
		return adaptor;
	}

	/**
	 * Returns the BundleFile for this BaseData.  The first time this method is called the
	 * configured storage {@link BaseAdaptor#createBundleFile(Object, BaseData)} method is called.
	 * @return the BundleFile
	 * @throws IllegalArgumentException
	 */
	public synchronized BundleFile getBundleFile() throws IllegalArgumentException {
		if (bundleFile == null)
			try {
				bundleFile = adaptor.createBundleFile(null, this);
			} catch (IOException e) {
				throw (IllegalArgumentException) new IllegalArgumentException(e.getMessage()).initCause(e);
			}
		return bundleFile;
	}

	public synchronized BundleFile getBundleFile(Object content, boolean base) {
		return base ? bundleFile : (bundleFiles == null) ? null : bundleFiles.get(content);
	}

	public synchronized void setBundleFile(Object content, BundleFile bundleFile) {
		if (bundleFiles == null)
			bundleFiles = new ArrayMap<Object, BundleFile>(1);
		bundleFiles.put(content, bundleFile);
	}

	private static String[] getClassPath(ManifestElement[] classpath) {
		if (classpath == null) {
			if (Debug.DEBUG_LOADER)
				Debug.println("  no classpath"); //$NON-NLS-1$
			/* create default BundleClassPath */
			return new String[] {"."}; //$NON-NLS-1$
		}

		List<String> result = new ArrayList<String>(classpath.length);
		for (int i = 0; i < classpath.length; i++) {
			if (Debug.DEBUG_LOADER)
				Debug.println("  found classpath entry " + classpath[i].getValueComponents()); //$NON-NLS-1$
			String[] paths = classpath[i].getValueComponents();
			for (int j = 0; j < paths.length; j++) {
				result.add(paths[j]);
			}
		}

		return result.toArray(new String[result.size()]);
	}

	/**
	 * Returns the storage hook which is keyed by the specified key
	 * @param key the key of the storage hook to get
	 * @return the storage hook which is keyed by the specified key
	 */
	public StorageHook getStorageHook(String key) {
		if (storageHooks == null)
			return null;
		for (int i = 0; i < storageHooks.length; i++)
			if (storageHooks[i].getKey().equals(key))
				return storageHooks[i];
		return null;
	}

	/**
	 * Sets the instance storage hooks for this base data.  This is method
	 * may only be called once for the lifetime of the base data.  Once set,
	 * the list of storage hooks remains constant.
	 * @param storageHooks the storage hook to add
	 */
	public void setStorageHooks(StorageHook[] storageHooks) {
		if (this.storageHooks != null)
			return; // only allow this to be set once.
		this.storageHooks = storageHooks;
	}

	/**
	 * Returns all the storage hooks associated with this BaseData
	 * @return all the storage hooks associated with this BaseData
	 */
	public StorageHook[] getStorageHooks() {
		return storageHooks == null ? new StorageHook[0] : storageHooks;
	}

	/**
	 * Gets called by BundleFile during {@link BundleFile#getFile(String, boolean)}.  This method 
	 * will allocate a File object where content of the specified path may be 
	 * stored for the current generation of the base data.  The returned File object may 
	 * not exist if the content has not previously be stored.
	 * @param path the path to the content to extract from the base data
	 * @return a file object where content of the specified path may be stored.
	 */
	public File getExtractFile(String path) {
		return adaptor.getStorage().getExtractFile(this, path);
	}

	/**
	 * This is only used to support PDE source lookup.  The field named &quot;fileName&quot; 
	 * must be set for PDE to access the absolute path string.
	 * @param fileName an absolute path string to the base bundle file. 
	 */
	// This is only done for PDE source lookup (bug 126517)
	public void setFileName(String fileName) {
		this.fileName = fileName;
	}

	/**
	 * Return a string representation of the bundle that can be used in debug messages.
	 * 
	 * @return String representation of the bundle
	 */
	public String toString() {
		String name = getSymbolicName();
		if (name == null)
			return getLocation();
		Version ver = getVersion();
		if (ver == null)
			return name;
		return name + "_" + ver; //$NON-NLS-1$
	}

	public Enumeration<URL> findLocalResources(String path) {
		String[] cp;
		try {
			cp = getClassPath();
		} catch (BundleException e) {
			cp = new String[0];
		}
		if (cp == null)
			cp = new String[0];
		// this is not optimized; degenerate case of someone calling getResource on an unresolved bundle!
		ClasspathManager cm = new ClasspathManager(this, cp, null);
		cm.initialize();
		Enumeration<URL> result = cm.findLocalResources(path);
		// no need to close ClassPathManager because the BundleFile objects are stored in the BaseData and closed on shutdown or uninstall
		return result;
	}
}

Back to the top