blob: 02cd5c01d2ecaa35edae55036543f4c4952b5665 [file] [log] [blame]
/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.aries.cdi.container.internal;
import static org.osgi.namespace.extender.ExtenderNamespace.EXTENDER_NAMESPACE;
import static org.osgi.service.cdi.CDIConstants.CDI_CAPABILITY_NAME;
import static org.osgi.service.cdi.CDIConstants.REQUIREMENT_BEANS_ATTRIBUTE;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.aries.cdi.container.internal.command.CDICommand;
import org.apache.aries.cdi.container.internal.container.CDIBundle;
import org.apache.aries.cdi.container.internal.container.ConfigurationListener;
import org.apache.aries.cdi.container.internal.container.ContainerBootstrap;
import org.apache.aries.cdi.container.internal.container.ContainerState;
import org.apache.aries.cdi.container.internal.container.ExtensionPhase;
import org.apache.aries.cdi.container.internal.model.ContainerActivator;
import org.apache.aries.cdi.container.internal.model.ContainerComponent;
import org.apache.aries.cdi.container.internal.model.FactoryActivator;
import org.apache.aries.cdi.container.internal.model.FactoryComponent;
import org.apache.aries.cdi.container.internal.model.SingleActivator;
import org.apache.aries.cdi.container.internal.model.SingleComponent;
import org.apache.aries.cdi.container.internal.spi.ContainerListener;
import org.apache.aries.cdi.container.internal.util.Logs;
import org.apache.aries.cdi.spi.CDIContainerInitializer;
import org.apache.felix.utils.extender.AbstractExtender;
import org.apache.felix.utils.extender.Extension;
import org.osgi.annotation.bundle.Header;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceObjects;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.cdi.runtime.CDIComponentRuntime;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.annotations.RequireConfigurationAdmin;
import org.osgi.service.log.Logger;
import org.osgi.service.log.LoggerFactory;
import org.osgi.util.promise.PromiseFactory;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
@Header(
name = Constants.BUNDLE_ACTIVATOR,
value = "${@class}"
)
@RequireConfigurationAdmin
public class Activator extends AbstractExtender {
private volatile CCR _ccr;
private volatile ExecutorService _executorService;
private volatile Logger _log;
private volatile Logs _logs;
private volatile PromiseFactory _promiseFactory;
private volatile ServiceTracker<CDIContainerInitializer, ServiceObjects<CDIContainerInitializer>> _containerTracker;
private volatile ServiceTracker<ContainerListener, ContainerListener> _containerListeners;
public Activator() {
setSynchronous(true);
}
@Override
public void start(BundleContext bundleContext) throws Exception {
_logs = new Logs.Builder(bundleContext).build();
_log = _logs.getLogger(Activator.class);
_containerTracker = new ServiceTracker<>(
bundleContext, CDIContainerInitializer.class,
new ServiceTrackerCustomizer<CDIContainerInitializer, ServiceObjects<CDIContainerInitializer>>() {
@Override
public ServiceObjects<CDIContainerInitializer> addingService(
ServiceReference<CDIContainerInitializer> reference) {
return bundleContext.getServiceObjects(reference);
}
@Override
public void modifiedService(ServiceReference<CDIContainerInitializer> reference,
ServiceObjects<CDIContainerInitializer> service) {
}
@Override
public void removedService(ServiceReference<CDIContainerInitializer> reference,
ServiceObjects<CDIContainerInitializer> service) {
}
}
);
_containerTracker.open();
_containerListeners = new ServiceTracker<>(bundleContext, ContainerListener.class, null);
_containerListeners.open();
_executorService = Executors.newSingleThreadExecutor(worker -> {
Thread t = new Thread(new ThreadGroup("Apache Aries CCR - CDI"), worker, "Aries CCR Thread (" + hashCode() + ")");
t.setDaemon(false);
return t;
});
_promiseFactory = new PromiseFactory(_executorService);
_ccr = new CCR(_promiseFactory, _logs);
_command = new CDICommand(_ccr);
if (_log.isDebugEnabled()) {
_log.debug("CCR starting {}", bundleContext.getBundle());
}
_bundleContext = bundleContext;
registerCCR();
registerCDICommand();
super.start(bundleContext);
if (_log.isDebugEnabled()) {
_log.debug("CCR started {}", bundleContext.getBundle());
}
}
private void registerCCR() {
Dictionary<String, Object> properties = new Hashtable<>();
properties.put(Constants.SERVICE_CHANGECOUNT, _ccrChangeCount.get());
properties.put(Constants.SERVICE_DESCRIPTION, "Aries CDI - CDI Component Runtime");
properties.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
ChangeObserverFactory changeObserverFactory = new ChangeObserverFactory();
_ccrChangeCount.addObserver(changeObserverFactory);
_ccrRegistration = _bundleContext.registerService(
CDIComponentRuntime.class, changeObserverFactory, properties);
}
private void registerCDICommand() {
Dictionary<String, Object> properties = new Hashtable<>();
properties.put("osgi.command.scope", "cdi");
properties.put("osgi.command.function", new String[] {"list", "info"});
properties.put(Constants.SERVICE_DESCRIPTION, "Aries CDI - Gogo Commands");
properties.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
_commandRegistration = _bundleContext.registerService(Object.class, _command, properties);
}
@Override
public void stop(BundleContext bundleContext) throws Exception {
if (_log.isDebugEnabled()) {
_log.debug("CCR stoping {}", bundleContext.getBundle());
}
super.stop(bundleContext);
_commandRegistration.unregister();
_ccrRegistration.unregister();
if (_log.isDebugEnabled()) {
_log.debug("CCR stoped {}", bundleContext.getBundle());
}
_executorService.shutdownNow();
_executorService.awaitTermination(2, TimeUnit.SECONDS); // not important but just to avoid to quit too fast
_containerTracker.close();
_containerListeners.close();
}
@Override
protected Extension doCreateExtension(Bundle bundle) throws Exception {
if (!requiresCDIExtender(bundle)) {
return null;
}
ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> caTracker = new ServiceTracker<>(
bundle.getBundleContext(), ConfigurationAdmin.class, null);
caTracker.open();
ServiceTracker<LoggerFactory, LoggerFactory> loggerTracker = new ServiceTracker<>(
bundle.getBundleContext(), LoggerFactory.class, null);
loggerTracker.open();
ContainerState containerState = new ContainerState(
bundle, _bundleContext.getBundle(), _ccrChangeCount, _promiseFactory, caTracker, _logs);
// the CDI bundle
return new CDIBundle(_ccr, containerState,
// handle extensions
new ExtensionPhase(containerState,
// listen for configurations of the container component
new ConfigurationListener.Builder(containerState).component(
// the container component
new ContainerComponent.Builder(containerState,
// when dependencies are satisfied activate the container
new ContainerActivator.Builder(containerState,
// when the active container bootstraps CDI
new ContainerBootstrap(
containerState, _containerTracker,
// when CDI is bootstrapping is complete and is up and running
// activate the configuration listeners for single and factory components
new ConfigurationListener.Builder(containerState),
new SingleComponent.Builder(containerState,
new SingleActivator.Builder(containerState)),
new FactoryComponent.Builder(containerState,
new FactoryActivator.Builder(containerState)),
_containerListeners
)
)
).build()
).build()
)
);
}
@Override
protected void debug(Bundle bundle, String msg) {
if (_log.isTraceEnabled()) {
_log.trace(msg, bundle);
}
}
@Override
protected void warn(Bundle bundle, String msg, Throwable t) {
if (_log.isWarnEnabled()) {
_log.warn(msg, bundle, t);
}
}
@Override
protected void error(String msg, Throwable t) {
if (_log.isErrorEnabled()) {
_log.error(msg, t);
}
}
private boolean requiresCDIExtender(Bundle bundle) {
BundleWiring bundleWiring = bundle.adapt(BundleWiring.class);
List<BundleWire> requiredBundleWires = bundleWiring.getRequiredWires(EXTENDER_NAMESPACE);
for (BundleWire bundleWire : requiredBundleWires) {
Map<String, Object> attributes = bundleWire.getCapability().getAttributes();
if (attributes.containsKey(EXTENDER_NAMESPACE) &&
attributes.get(EXTENDER_NAMESPACE).equals(CDI_CAPABILITY_NAME)) {
Bundle providerWiringBundle = bundleWire.getProviderWiring().getBundle();
if (providerWiringBundle.equals(_bundleContext.getBundle())) {
BundleRequirement requirement = bundleWire.getRequirement();
Map<String, Object> requirementAttributes = requirement.getAttributes();
@SuppressWarnings("unchecked")
List<String> beans = (List<String>)requirementAttributes.get(REQUIREMENT_BEANS_ATTRIBUTE);
if (beans != null && !beans.isEmpty()) {
return true;
}
}
}
}
return false;
}
private BundleContext _bundleContext;
private final ChangeCount _ccrChangeCount = new ChangeCount();
private ServiceRegistration<CDIComponentRuntime> _ccrRegistration;
private volatile CDICommand _command;
private ServiceRegistration<?> _commandRegistration;
private class ChangeObserverFactory implements Observer, ServiceFactory<CDIComponentRuntime> {
@Override
public CDIComponentRuntime getService(
Bundle bundle,
ServiceRegistration<CDIComponentRuntime> registration) {
_registrations.add(registration);
return _ccr;
}
@Override
public void ungetService(
Bundle bundle, ServiceRegistration<CDIComponentRuntime> registration,
CDIComponentRuntime service) {
_registrations.remove(registration);
}
@Override
public void update(Observable o, Object arg) {
if (!(o instanceof ChangeCount)) {
return;
}
ChangeCount changeCount = (ChangeCount)o;
for (ServiceRegistration<CDIComponentRuntime> registration : _registrations) {
Dictionary<String, Object> properties = registration.getReference().getProperties();
properties.put(Constants.SERVICE_CHANGECOUNT, changeCount.get());
registration.setProperties(properties);
}
}
private final List<ServiceRegistration<CDIComponentRuntime>> _registrations = new CopyOnWriteArrayList<>();
}
}