All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.osgi.internal.framework.OSGiFrameworkHooks Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2012, 2013 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.internal.framework;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;
import org.eclipse.osgi.container.*;
import org.eclipse.osgi.container.Module.State;
import org.eclipse.osgi.framework.util.ArrayMap;
import org.eclipse.osgi.internal.debug.Debug;
import org.eclipse.osgi.internal.messages.Msg;
import org.eclipse.osgi.internal.serviceregistry.*;
import org.eclipse.osgi.report.resolution.ResolutionReport;
import org.eclipse.osgi.storage.Storage;
import org.eclipse.osgi.util.NLS;
import org.osgi.framework.*;
import org.osgi.framework.hooks.bundle.CollisionHook;
import org.osgi.framework.hooks.resolver.ResolverHook;
import org.osgi.framework.hooks.resolver.ResolverHookFactory;
import org.osgi.framework.wiring.*;

class OSGiFrameworkHooks {
	static final String collisionHookName = CollisionHook.class.getName();
	private final CoreResolverHookFactory resolverHookFactory;
	private final ModuleCollisionHook collisionHook;

	OSGiFrameworkHooks(EquinoxContainer container, Storage storage) {
		resolverHookFactory = new CoreResolverHookFactory(container, storage);
		collisionHook = new BundleCollisionHook(container);
	}

	public ResolverHookFactory getResolverHookFactory() {
		return resolverHookFactory;
	}

	public ModuleCollisionHook getModuleCollisionHook() {
		return collisionHook;
	}

	static class BundleCollisionHook implements ModuleCollisionHook {
		final Debug debug;
		final EquinoxContainer container;

		public BundleCollisionHook(EquinoxContainer container) {
			this.container = container;
			this.debug = container.getConfiguration().getDebug();
		}

		@Override
		public void filterCollisions(int operationType, Module target, Collection collisionCandidates) {
			switch (container.getConfiguration().BSN_VERSION) {
				case EquinoxConfiguration.BSN_VERSION_SINGLE : {
					return;
				}
				case EquinoxConfiguration.BSN_VERSION_MULTIPLE : {
					collisionCandidates.clear();
					return;
				}
				case EquinoxConfiguration.BSN_VERSION_MANAGED : {
					Bundle targetBundle = target.getBundle();
					ArrayMap candidateBundles = new ArrayMap(collisionCandidates.size());
					for (Module module : collisionCandidates) {
						candidateBundles.put(module.getBundle(), module);
					}
					notifyCollisionHooks(operationType, targetBundle, candidateBundles);
					collisionCandidates.retainAll(candidateBundles.getValues());
					return;
				}
				default :
					throw new IllegalStateException("Bad configuration: " + container.getConfiguration().BSN_VERSION); //$NON-NLS-1$
			}
		}

		private void notifyCollisionHooks(final int operationType, final Bundle target, Collection collisionCandidates) {
			// Note that collision hook results are honored for the system bundle.
			final Collection shrinkable = new ShrinkableCollection(collisionCandidates);
			if (System.getSecurityManager() == null) {
				notifyCollisionHooksPriviledged(operationType, target, shrinkable);
			} else {
				AccessController.doPrivileged(new PrivilegedAction() {
					public Void run() {
						notifyCollisionHooksPriviledged(operationType, target, shrinkable);
						return null;
					}
				});
			}
		}

		void notifyCollisionHooksPriviledged(final int operationType, final Bundle target, final Collection collisionCandidates) {
			if (debug.DEBUG_HOOKS) {
				Debug.println("notifyCollisionHooks(" + operationType + ", " + target + ", " + collisionCandidates + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ 
			}
			ServiceRegistry registry = container.getServiceRegistry();
			if (registry != null) {
				registry.notifyHooksPrivileged(new HookContext() {
					public void call(Object hook, ServiceRegistration hookRegistration) throws Exception {
						if (hook instanceof CollisionHook) {
							((CollisionHook) hook).filterCollisions(operationType, target, collisionCandidates);
						}
					}

					public String getHookClassName() {
						return collisionHookName;
					}

					public String getHookMethodName() {
						return "filterCollisions"; //$NON-NLS-1$ 
					}
				});
			}
		}
	}

	/**
	 * This class encapsulates the delegation to ResolverHooks that are registered with the service
	 * registry.  This way the resolver implementation only has to call out to a single hook
	 * which does all the necessary service registry lookups.
	 * 
	 * This class is not thread safe and expects external synchronization.
	 *
	 */
	static class CoreResolverHookFactory implements ResolverHookFactory {
		// need a tuple to hold the service reference and hook object
		// do not use a map for performance reasons; no need to hash based on a key.
		static class HookReference {
			public HookReference(ServiceReferenceImpl reference, ResolverHook hook, BundleContextImpl context) {
				this.reference = reference;
				this.hook = hook;
				this.context = context;
			}

			final ServiceReferenceImpl reference;
			final ResolverHook hook;
			final BundleContextImpl context;
		}

		final Debug debug;
		final EquinoxContainer container;
		final Storage storage;
		volatile boolean inInit = false;

		public CoreResolverHookFactory(EquinoxContainer container, Storage storage) {
			this.container = container;
			this.debug = container.getConfiguration().getDebug();
			this.storage = storage;
		}

		void handleHookException(Throwable t, Object hook, String method) {
			if (debug.DEBUG_HOOKS) {
				Debug.println(hook.getClass().getName() + "." + method + "() exception:"); //$NON-NLS-1$ //$NON-NLS-2$
				if (t != null)
					Debug.printStackTrace(t);
			}
			String message = NLS.bind(Msg.SERVICE_FACTORY_EXCEPTION, hook.getClass().getName(), method);
			throw new RuntimeException(message, new BundleException(message, BundleException.REJECTED_BY_HOOK, t));
		}

		private ServiceReferenceImpl[] getHookReferences(ServiceRegistry registry, BundleContextImpl context) {
			try {
				@SuppressWarnings("unchecked")
				ServiceReferenceImpl[] result = (ServiceReferenceImpl[]) registry.getServiceReferences(context, ResolverHookFactory.class.getName(), null, false);
				return result;
			} catch (InvalidSyntaxException e) {
				// cannot happen; no filter
				return null;
			}
		}

		public ResolverHook begin(Collection triggers) {
			if (debug.DEBUG_HOOKS) {
				Debug.println("ResolverHook.begin"); //$NON-NLS-1$
			}
			ModuleContainer mContainer = storage.getModuleContainer();
			Module systemModule = mContainer == null ? null : mContainer.getModule(0);
			ServiceRegistry registry = container.getServiceRegistry();
			if (registry == null || systemModule == null) {
				return new CoreResolverHook(Collections. emptyList(), systemModule);
			}

			BundleContextImpl context = (BundleContextImpl) EquinoxContainer.secureAction.getContext(systemModule.getBundle());

			ServiceReferenceImpl[] refs = getHookReferences(registry, context);
			List hookRefs = refs == null ? Collections. emptyList() : new ArrayList(refs.length);
			if (refs != null) {
				for (ServiceReferenceImpl hookRef : refs) {
					ResolverHookFactory factory = context.getService(hookRef);
					if (factory != null) {
						try {
							ResolverHook hook = factory.begin(triggers);
							if (hook != null)
								hookRefs.add(new HookReference(hookRef, hook, context));
						} catch (Throwable t) {
							// need to force an end call on the ResolverHooks we got and release them
							try {
								new CoreResolverHook(hookRefs, systemModule).end();
							} catch (Throwable endError) {
								// we are already in failure mode; just continue
							}
							handleHookException(t, factory, "begin"); //$NON-NLS-1$
						}
					}
				}
			}
			return new CoreResolverHook(hookRefs, systemModule);
		}

		class CoreResolverHook implements ResolutionReport.Listener, ResolverHook {
			private final List hooks;
			private final Module systemModule;

			private volatile ResolutionReport resolutionReport;

			CoreResolverHook(List hooks, Module systemModule) {
				this.hooks = hooks;
				this.systemModule = systemModule;
			}

			public void filterResolvable(Collection candidates) {
				if (debug.DEBUG_HOOKS) {
					Debug.println("ResolverHook.filterResolvable(" + candidates + ")"); //$NON-NLS-1$ //$NON-NLS-2$
				}
				if (isBootInit()) {
					// only allow the system bundle and its fragments resolve during boot up and init
					for (Iterator iCandidates = candidates.iterator(); iCandidates.hasNext();) {
						BundleRevision revision = iCandidates.next();
						if ((revision.getTypes() & BundleRevision.TYPE_FRAGMENT) == 0) {
							// host bundle; check if it is the system bundle
							if (revision.getBundle().getBundleId() != 0) {
								iCandidates.remove();
							}
						}
						// just leave all fragments.  Only the ones that are system bundle fragments will resolve
						// since we removed all the other possible hosts.
					}
				}
				if (hooks.isEmpty())
					return;
				candidates = new ShrinkableCollection(candidates);
				for (Iterator iHooks = hooks.iterator(); iHooks.hasNext();) {
					HookReference hookRef = iHooks.next();
					if (hookRef.reference.getBundle() == null) {
						handleHookException(null, hookRef.hook, "filterResolvable"); //$NON-NLS-1$
					} else {
						try {
							hookRef.hook.filterResolvable(candidates);
						} catch (Throwable t) {
							handleHookException(t, hookRef.hook, "filterResolvable"); //$NON-NLS-1$
						}
					}
				}
			}

			private boolean isBootInit() {
				return systemModule == null || !Module.RESOLVED_SET.contains(systemModule.getState()) || (systemModule.getState().equals(State.STARTING) && inInit);
			}

			public void filterSingletonCollisions(BundleCapability singleton, Collection collisionCandidates) {
				if (debug.DEBUG_HOOKS) {
					Debug.println("ResolverHook.filterSingletonCollisions(" + singleton + ", " + collisionCandidates + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				}
				if (hooks.isEmpty())
					return;
				collisionCandidates = new ShrinkableCollection(collisionCandidates);
				for (Iterator iHooks = hooks.iterator(); iHooks.hasNext();) {
					HookReference hookRef = iHooks.next();
					if (hookRef.reference.getBundle() == null) {
						handleHookException(null, hookRef.hook, "filterSingletonCollisions"); //$NON-NLS-1$
					} else {
						try {
							hookRef.hook.filterSingletonCollisions(singleton, collisionCandidates);
						} catch (Throwable t) {
							handleHookException(t, hookRef.hook, "filterSingletonCollisions"); //$NON-NLS-1$
						}
					}
				}
			}

			public void filterMatches(BundleRequirement requirement, Collection candidates) {
				if (debug.DEBUG_HOOKS) {
					Debug.println("ResolverHook.filterMatches(" + requirement + ", " + candidates + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				}
				if (hooks.isEmpty())
					return;
				candidates = new ShrinkableCollection(candidates);
				for (Iterator iHooks = hooks.iterator(); iHooks.hasNext();) {
					HookReference hookRef = iHooks.next();
					if (hookRef.reference.getBundle() == null) {
						handleHookException(null, hookRef.hook, "filterMatches"); //$NON-NLS-1$
					} else {
						try {
							hookRef.hook.filterMatches(requirement, candidates);
						} catch (Throwable t) {
							handleHookException(t, hookRef.hook, "filterMatches"); //$NON-NLS-1$
						}
					}
				}
			}

			public void end() {
				if (debug.DEBUG_HOOKS) {
					Debug.println("ResolverHook.end"); //$NON-NLS-1$
				}
				if (hooks.isEmpty())
					return;
				try {
					HookReference missingHook = null;
					Throwable endError = null;
					HookReference endBadHook = null;
					for (Iterator iHooks = hooks.iterator(); iHooks.hasNext();) {
						HookReference hookRef = iHooks.next();
						// We do not remove unregistered services here because we are going to remove all of them at the end
						if (hookRef.reference.getBundle() == null) {
							if (missingHook == null)
								missingHook = hookRef;
						} else {
							try {
								if (hookRef.hook instanceof ResolutionReport.Listener)
									((ResolutionReport.Listener) hookRef.hook).handleResolutionReport(resolutionReport);
								hookRef.hook.end();
							} catch (Throwable t) {
								// Must continue on to the next hook.end method
								// save the error for throwing at the end
								if (endError == null) {
									endError = t;
									endBadHook = hookRef;
								}
							}
						}
					}
					if (missingHook != null)
						handleHookException(null, missingHook.hook, "end"); //$NON-NLS-1$
					if (endError != null)
						handleHookException(endError, endBadHook.hook, "end"); //$NON-NLS-1$
				} finally {
					for (HookReference hookRef : hooks) {
						hookRef.context.ungetService(hookRef.reference);
					}
					hooks.clear();
				}
			}

			@Override
			public void handleResolutionReport(ResolutionReport report) {
				resolutionReport = report;
			}
		}
	}

	public void initBegin() {
		resolverHookFactory.inInit = true;
	}

	public void initEnd() {
		resolverHookFactory.inInit = false;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy