blob: b1478ea0e05643193007ac291fc23671967c8293 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2017 Markus Alexander Kuppe 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:
* Markus Alexander Kuppe - initial API and implementation
* Lars.Vogel <Lars.Vogel@vogella.com> - Bug 472654
******************************************************************************/
package org.eclipse.e4.core.di.internal.extensions;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.InjectionException;
import org.eclipse.e4.core.di.annotations.Optional;
import org.eclipse.e4.core.di.suppliers.ExtendedObjectSupplier;
import org.eclipse.e4.core.di.suppliers.IObjectDescriptor;
import org.eclipse.e4.core.di.suppliers.IRequestor;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
@Component(service = { ExtendedObjectSupplier.class, EventHandler.class }, property = {
"dependency.injection.annotation=org.eclipse.e4.core.di.extensions.OSGiBundle",
"event.topics=" + IEclipseContext.TOPIC_DISPOSE })
public class OSGiObjectSupplier extends ExtendedObjectSupplier implements EventHandler {
/**
* A Map of Requestor to BundleListener. Each Requestor will only ever request its own bundle and thus there is a 1:1 relationship between R and BL.
*/
private final Map<IRequestor, BundleListener> requestor2listener = new HashMap<>();
private BundleContext localBundleContext;
@Activate
void activate(BundleContext context) {
this.localBundleContext = context;
}
@Override
public Object get(IObjectDescriptor descriptor, IRequestor requestor, boolean track, boolean group) {
final Class<?> requestingObjectClass = requestor.getRequestingObjectClass();
final Type desiredType = descriptor.getDesiredType();
if (BundleContext.class.equals(desiredType)) {
final Bundle bundle = FrameworkUtil.getBundle(requestingObjectClass);
// Cannot use BundleListener as a BL can only be registered with a BC (which might be null)
// Iff track is request and there is no listener yet, lets track the bundle
if (track) {
if (!requestor2listener.containsKey(requestor)) {
track(bundle, requestor);
}
// Handlers only executed once and thus don't track the BC/Bundle.
// Still guard to now de-register a non-existing listener.
} else if (requestor2listener.containsKey(requestor)) {
untrack(requestor);
}
final BundleContext bundleContext = bundle.getBundleContext();
if (bundleContext != null) {
return bundleContext;
} else if (descriptor.getQualifier(Optional.class) != null) {
// Do not have a bundle context but requestor has marked the parameter/field optional
return null;
}
throw new InjectionException("Unable to inject BundleContext: " + bundle.getSymbolicName() + " bundle is not active or starting/stopping"); //$NON-NLS-1$ //$NON-NLS-2$
} else if (Bundle.class.equals(desiredType)) {
// Not tracking the Bundle's life-cycle because the B instance does
// not change whether a bundle is ACTIVE or RESOLVED. The only
// thing worth tracking is when a bundle switches to the INSTALLED
// state. However, the requestor will go away along with its bundle anyway.
return FrameworkUtil.getBundle(requestingObjectClass);
}
// Annotation used with unsupported type
return null;
}
private void untrack(final IRequestor requestor) {
synchronized (requestor2listener) {
BundleListener l = requestor2listener.remove(requestor);
localBundleContext.removeBundleListener(l);
}
}
private void track(final Bundle bundle, final IRequestor requestor) {
// A _synchronous_ BundleListener asserts that the BC is un-injected,
// _before_ it becomes invalid (state-wise).
BundleListener listener = new SynchronousBundleListener() {
@Override
public void bundleChanged(BundleEvent event) {
if (event.getBundle().equals(bundle)) {
if (requestor.isValid()) {
requestor.resolveArguments(false);
requestor.execute();
}
}
}
};
synchronized (requestor2listener) {
localBundleContext.addBundleListener(listener);
requestor2listener.put(requestor, listener);
}
}
@Override
public void handleEvent(Event event) {
for (Iterator<Entry<IRequestor, BundleListener>> it = requestor2listener.entrySet().iterator(); it.hasNext();) {
Entry<IRequestor, BundleListener> entry = it.next();
if (!entry.getKey().isValid()) {
localBundleContext.removeBundleListener(entry.getValue());
it.remove();
}
}
}
}