Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 248a8b6bc8733a95fa7ff108c64b40c685cdd787 (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
/*******************************************************************************
 * Copyright (c) 2012, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.osgi.internal.framework;

import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.osgi.container.Module;
import org.eclipse.osgi.container.Module.Settings;
import org.eclipse.osgi.container.Module.State;
import org.eclipse.osgi.container.ModuleCollisionHook;
import org.eclipse.osgi.container.ModuleContainerAdaptor;
import org.eclipse.osgi.container.ModuleLoader;
import org.eclipse.osgi.container.ModuleRevision;
import org.eclipse.osgi.container.ModuleRevisionBuilder;
import org.eclipse.osgi.container.ModuleWiring;
import org.eclipse.osgi.container.SystemModule;
import org.eclipse.osgi.internal.container.AtomicLazyInitializer;
import org.eclipse.osgi.internal.hookregistry.ClassLoaderHook;
import org.eclipse.osgi.internal.loader.BundleLoader;
import org.eclipse.osgi.internal.loader.FragmentLoader;
import org.eclipse.osgi.internal.loader.SystemBundleLoader;
import org.eclipse.osgi.internal.permadmin.BundlePermissions;
import org.eclipse.osgi.service.debug.DebugOptions;
import org.eclipse.osgi.storage.BundleInfo.Generation;
import org.eclipse.osgi.storage.Storage;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.hooks.resolver.ResolverHookFactory;
import org.osgi.framework.wiring.BundleRevision;

public class EquinoxContainerAdaptor extends ModuleContainerAdaptor {
	public static final ClassLoader BOOT_CLASSLOADER;
	static {
		ClassLoader platformClassLoader = null;
		try {
			Method getPlatformClassLoader = ClassLoader.class.getMethod("getPlatformClassLoader"); //$NON-NLS-1$
			platformClassLoader = (ClassLoader) getPlatformClassLoader.invoke(null);
		} catch (Throwable t) {
			// try everything possible to not fail <clinit>
			platformClassLoader = new ClassLoader(Object.class.getClassLoader()) {
				/* boot class loader */};
		}
		BOOT_CLASSLOADER = platformClassLoader;
	}
	private final EquinoxContainer container;
	private final Storage storage;
	private final OSGiFrameworkHooks hooks;
	private final Map<Long, Generation> initial;
	// The ClassLoader parent to use when creating ModuleClassLoaders.
	private final ClassLoader moduleClassLoaderParent;
	private final AtomicLong lastSecurityAdminFlush;

	final AtomicLazyInitializer<Executor> resolverExecutor;
	final Callable<Executor> lazyResolverExecutorCreator;
	final AtomicLazyInitializer<Executor> startLevelExecutor;
	final Callable<Executor> lazyStartLevelExecutorCreator;

	public EquinoxContainerAdaptor(EquinoxContainer container, Storage storage, Map<Long, Generation> initial) {
		this.container = container;
		this.storage = storage;
		this.hooks = new OSGiFrameworkHooks(container, storage);
		this.initial = initial;
		this.moduleClassLoaderParent = getModuleClassLoaderParent(container.getConfiguration());
		this.lastSecurityAdminFlush = new AtomicLong();

		EquinoxConfiguration config = container.getConfiguration();
		@SuppressWarnings("deprecation")
		String resolverThreadCntProp = config.getConfiguration(EquinoxConfiguration.PROP_EQUINOX_RESOLVER_THREAD_COUNT, //
				config.getConfiguration(EquinoxConfiguration.PROP_RESOLVER_THREAD_COUNT));

		int resolverThreadCnt;
		try {
			// note that resolver thread count defaults to -1 (compute based on processor number)
			resolverThreadCnt = resolverThreadCntProp == null ? -1 : Integer.parseInt(resolverThreadCntProp);
		} catch (NumberFormatException e) {
			resolverThreadCnt = -1;
		}
		String startLevelThreadCntProp = config.getConfiguration(EquinoxConfiguration.PROP_EQUINOX_START_LEVEL_THREAD_COUNT);
		int startLevelThreadCnt;
		try {
			// Note that start-level thread count defaults to 1 (synchronous start)
			startLevelThreadCnt = startLevelThreadCntProp == null ? 1 : Integer.parseInt(startLevelThreadCntProp);
		} catch (NumberFormatException e) {
			startLevelThreadCnt = 1;
		}

		// Use two different executors for resolver and start-level because of the different queue requirements

		// For the resolver we must use a SynchronousQueue because multiple threads
		// can kick off a resolution operation and block one of the executor threads
		// per resolution operation.
		// If the number of concurrent resolution operations reaches the number of
		// executor threads then each executor thread may end up blocked causing the
		// executor to no longer accept work.  A SynchronousQueue prevents that from
		// happening.
		this.resolverExecutor = new AtomicLazyInitializer<>();
		this.lazyResolverExecutorCreator = createLazyExecutorCreator( //
				"Equinox resolver thread - " + EquinoxContainerAdaptor.this.toString(), //$NON-NLS-1$
				resolverThreadCnt, new SynchronousQueue<Runnable>());

		// For the start-level we can safely use a growing queue because the thread feeding the
		// start-level executor with work is a single thread and it can safely block waiting
		// for the work of the executor threads to finish.
		this.startLevelExecutor = new AtomicLazyInitializer<>();
		this.lazyStartLevelExecutorCreator = createLazyExecutorCreator(//
				"Equinox start level thread - " + EquinoxContainerAdaptor.this.toString(), //$NON-NLS-1$
				startLevelThreadCnt, new LinkedBlockingQueue<Runnable>(1000));

	}

	private Callable<Executor> createLazyExecutorCreator(final String threadName, int threadCnt, final BlockingQueue<Runnable> queue) {
		// use the number of processors when configured value is <=0
		final int maxThreads = threadCnt <= 0 ? Runtime.getRuntime().availableProcessors() : threadCnt;
		return new Callable<Executor>() {
			@Override
			public Executor call() throws Exception {
				if (maxThreads == 1) {
					// just do synchronous execution with current thread
					return new Executor() {
						@Override
						public void execute(Runnable command) {
							command.run();
						}
					};
				}
				// Always want to create core threads until max size
				int coreThreads = maxThreads;
				// idle timeout; make it short to get rid of threads quickly after use
				int idleTimeout = 10;
				// try to name the threads with useful name
				ThreadFactory threadFactory = new ThreadFactory() {
					@Override
					public Thread newThread(Runnable r) {
						Thread t = new Thread(r, threadName);
						t.setDaemon(true);
						return t;
					}
				};
				// use a rejection policy that simply runs the task in the current thread once the max pool size is reached
				RejectedExecutionHandler rejectHandler = new ThreadPoolExecutor.CallerRunsPolicy();

				ThreadPoolExecutor executor = new ThreadPoolExecutor(coreThreads, maxThreads, idleTimeout, TimeUnit.SECONDS, queue, threadFactory, rejectHandler);
				executor.allowCoreThreadTimeOut(true);
				return executor;
			}
		};
	}

	private static ClassLoader getModuleClassLoaderParent(EquinoxConfiguration configuration) {
		// allow hooks to determine the parent class loader
		for (ClassLoaderHook hook : configuration.getHookRegistry().getClassLoaderHooks()) {
			ClassLoader parent = hook.getModuleClassLoaderParent(configuration);
			if (parent != null) {
				// first one to return non-null wins.
				return parent;
			}
		}
		// DEFAULT behavior:
		// check property for specified parent
		// check the osgi defined property first
		String type = configuration.getConfiguration(Constants.FRAMEWORK_BUNDLE_PARENT);
		if (type == null) {
			type = configuration.getConfiguration(EquinoxConfiguration.PROP_PARENT_CLASSLOADER, Constants.FRAMEWORK_BUNDLE_PARENT_BOOT);
		}

		if (Constants.FRAMEWORK_BUNDLE_PARENT_FRAMEWORK.equalsIgnoreCase(type) || EquinoxConfiguration.PARENT_CLASSLOADER_FWK.equalsIgnoreCase(type)) {
			ClassLoader cl = EquinoxContainer.class.getClassLoader();
			return cl == null ? BOOT_CLASSLOADER : cl;
		}
		if (Constants.FRAMEWORK_BUNDLE_PARENT_APP.equalsIgnoreCase(type))
			return ClassLoader.getSystemClassLoader();
		if (Constants.FRAMEWORK_BUNDLE_PARENT_EXT.equalsIgnoreCase(type)) {
			ClassLoader appCL = ClassLoader.getSystemClassLoader();
			if (appCL != null)
				return appCL.getParent();
		}
		return BOOT_CLASSLOADER;

	}

	@Override
	public ModuleCollisionHook getModuleCollisionHook() {
		return hooks.getModuleCollisionHook();
	}

	@Override
	public ResolverHookFactory getResolverHookFactory() {
		return hooks.getResolverHookFactory();
	}

	@Override
	public void publishContainerEvent(ContainerEvent type, Module module, Throwable error, FrameworkListener... listeners) {
		EquinoxEventPublisher publisher = container.getEventPublisher();
		if (publisher != null) {
			publisher.publishFrameworkEvent(getType(type), module.getBundle(), error, listeners);
		}
	}

	@Override
	public void publishModuleEvent(ModuleEvent type, Module module, Module origin) {
		EquinoxEventPublisher publisher = container.getEventPublisher();
		if (publisher != null) {
			publisher.publishBundleEvent(getType(type), module.getBundle(), origin.getBundle());
		}
	}

	@Override
	public Module createModule(String location, long id, EnumSet<Settings> settings, int startlevel) {
		EquinoxBundle bundle = new EquinoxBundle(id, location, storage.getModuleContainer(), settings, startlevel, container);
		return bundle.getModule();
	}

	@Override
	public SystemModule createSystemModule() {
		return (SystemModule) new EquinoxBundle.SystemBundle(storage.getModuleContainer(), container).getModule();
	}

	@Override
	public String getProperty(String key) {
		return storage.getConfiguration().getConfiguration(key);
	}

	@Override
	public ModuleLoader createModuleLoader(ModuleWiring wiring) {
		if (wiring.getBundle().getBundleId() == 0) {
			ClassLoader cl = EquinoxContainer.class.getClassLoader();
			cl = cl == null ? BOOT_CLASSLOADER : cl;
			return new SystemBundleLoader(wiring, container, cl);
		}
		if ((wiring.getRevision().getTypes() & BundleRevision.TYPE_FRAGMENT) != 0) {
			return new FragmentLoader();
		}
		return new BundleLoader(wiring, container, moduleClassLoaderParent);
	}

	@Override
	public Generation getRevisionInfo(String location, long id) {
		return initial.remove(id);
	}

	@Override
	public void associateRevision(ModuleRevision revision, Object revisionInfo) {
		((Generation) revisionInfo).setRevision(revision);
	}

	@Override
	public void invalidateWiring(ModuleWiring moduleWiring, ModuleLoader current) {
		if (current instanceof BundleLoader) {
			BundleLoader bundleLoader = (BundleLoader) current;
			bundleLoader.close();
		}
		long updatedTimestamp = storage.getModuleDatabase().getRevisionsTimestamp();
		if (System.getSecurityManager() != null && updatedTimestamp != lastSecurityAdminFlush.getAndSet(updatedTimestamp)) {
			storage.getSecurityAdmin().clearCaches();
			List<Module> modules = storage.getModuleContainer().getModules();
			for (Module module : modules) {
				for (ModuleRevision revision : module.getRevisions().getModuleRevisions()) {
					Generation generation = (Generation) revision.getRevisionInfo();
					if (generation != null) {
						ProtectionDomain domain = generation.getDomain();
						if (domain != null) {
							((BundlePermissions) domain.getPermissions()).clearPermissionCache();
						}
					}
				}
			}
		}
		clearManifestCache(moduleWiring);
	}

	private void clearManifestCache(ModuleWiring moduleWiring) {
		boolean frameworkActive = Module.ACTIVE_SET.contains(storage.getModuleContainer().getModule(0).getState());
		ModuleRevision revision = moduleWiring.getRevision();
		Module module = revision.getRevisions().getModule();
		boolean isUninstallingOrUninstalled = State.UNINSTALLED.equals(module.getState()) ^ module.holdsTransitionEventLock(ModuleEvent.UNINSTALLED);
		if (!frameworkActive || !isUninstallingOrUninstalled) {
			// only do this when the framework is not active or when the bundle is not uninstalled
			Generation generation = (Generation) moduleWiring.getRevision().getRevisionInfo();
			generation.clearManifestCache();
		}
	}

	static int getType(ContainerEvent type) {
		switch (type) {
			case ERROR :
				return FrameworkEvent.ERROR;
			case INFO :
				return FrameworkEvent.INFO;
			case WARNING :
				return FrameworkEvent.WARNING;
			case REFRESH :
				return FrameworkEvent.PACKAGES_REFRESHED;
			case START_LEVEL :
				return FrameworkEvent.STARTLEVEL_CHANGED;
			case STARTED :
				return FrameworkEvent.STARTED;
			case STOPPED :
				return FrameworkEvent.STOPPED;
			case STOPPED_REFRESH :
				return FrameworkEvent.STOPPED_SYSTEM_REFRESHED;
			case STOPPED_UPDATE :
				return FrameworkEvent.STOPPED_UPDATE;
			case STOPPED_TIMEOUT :
				return FrameworkEvent.WAIT_TIMEDOUT;
			default :
				// default to error
				return FrameworkEvent.ERROR;
		}
	}

	private int getType(ModuleEvent type) {
		switch (type) {
			case INSTALLED :
				return BundleEvent.INSTALLED;
			case LAZY_ACTIVATION :
				return BundleEvent.LAZY_ACTIVATION;
			case RESOLVED :
				return BundleEvent.RESOLVED;
			case STARTED :
				return BundleEvent.STARTED;
			case STARTING :
				return BundleEvent.STARTING;
			case STOPPING :
				return BundleEvent.STOPPING;
			case STOPPED :
				return BundleEvent.STOPPED;
			case UNINSTALLED :
				return BundleEvent.UNINSTALLED;
			case UNRESOLVED :
				return BundleEvent.UNRESOLVED;
			case UPDATED :
				return BundleEvent.UPDATED;
			default :
				// TODO log error?
				return 0;
		}
	}

	@Override
	public void refreshedSystemModule() {
		storage.getConfiguration().setConfiguration(EquinoxConfiguration.PROP_FORCED_RESTART, "true"); //$NON-NLS-1$
	}

	@Override
	public String toString() {
		return container.toString();
	}

	@Override
	public void updatedDatabase() {
		StorageSaver saver = container.getStorageSaver();
		if (saver == null)
			return;
		saver.save();
	}

	@Override
	public void initBegin() {
		hooks.initBegin();
	}

	@Override
	public void initEnd() {
		hooks.initEnd();
	}

	@Override
	public DebugOptions getDebugOptions() {
		return container.getConfiguration().getDebugOptions();
	}

	@Override
	public Executor getResolverExecutor() {
		return resolverExecutor.getInitialized(lazyResolverExecutorCreator);
	}

	@Override
	public Executor getStartLevelExecutor() {
		return startLevelExecutor.getInitialized(lazyStartLevelExecutorCreator);
	}

	@Override
	public ScheduledExecutorService getScheduledExecutor() {
		return container.getScheduledExecutor();
	}

	public void shutdownExecutors() {
		Executor current = resolverExecutor.getAndClear();
		if (current instanceof ExecutorService) {
			((ExecutorService) current).shutdown();
		}
		current = startLevelExecutor.getAndClear();
		if (current instanceof ExecutorService) {
			((ExecutorService) current).shutdown();
		}
	}

	@Override
	public ModuleRevisionBuilder adaptModuleRevisionBuilder(ModuleEvent operation, Module origin, ModuleRevisionBuilder builder, Object revisionInfo) {
		Generation generation = (Generation) revisionInfo;
		return generation.adaptModuleRevisionBuilder(operation, origin, builder);
	}
}

Back to the top