blob: 4771a276c05f4dca882dde4bdf267fbb971eff12 [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.container;
import static aQute.lib.exceptions.FunctionWithException.asFunction;
import static java.util.Objects.requireNonNull;
import static java.util.Optional.ofNullable;
import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY;
import java.net.URL;
import java.util.Collections;
import java.util.Dictionary;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Stream;
import javax.enterprise.inject.spi.Extension;
import org.apache.aries.cdi.container.internal.container.Op.Mode;
import org.apache.aries.cdi.container.internal.container.Op.Type;
import org.apache.aries.cdi.container.internal.loader.BundleClassLoader;
import org.apache.aries.cdi.container.internal.model.ExtendedExtensionDTO;
import org.apache.aries.cdi.container.internal.model.FactoryComponent;
import org.apache.aries.cdi.container.internal.model.OSGiBean;
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.Maps;
import org.apache.aries.cdi.container.internal.util.Syncro;
import org.apache.aries.cdi.spi.CDIContainerInitializer;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceObjects;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.resource.Capability;
import org.osgi.service.cdi.runtime.dto.ExtensionDTO;
import org.osgi.service.log.Logger;
import org.osgi.util.tracker.ServiceTracker;
public class ContainerBootstrap extends Phase {
public ContainerBootstrap(
ContainerState containerState,
ServiceTracker<CDIContainerInitializer, ServiceObjects<CDIContainerInitializer>> containerTracker,
ConfigurationListener.Builder configurationBuilder,
SingleComponent.Builder singleBuilder,
FactoryComponent.Builder factoryBuilder,
ServiceTracker<ContainerListener, ContainerListener> listeners) {
super(containerState, null);
_configurationBuilder = configurationBuilder;
_containerTracker = containerTracker;
_listeners = listeners;
_singleBuilder = singleBuilder;
_factoryBuilder = factoryBuilder;
_log = containerState.containerLogs().getLogger(getClass());
_serviceObjects = requireNonNull(
_containerTracker.getService(),
"A prototype scope org.apache.aries.cdi.spi.CDIContainerInitializer service must be available.");
}
@Override
public boolean close() {
try (Syncro syncro = _lock.open()) {
if (_containerInstance != null) {
_log.debug(l -> l.debug("CCR container shutdown for {}", bundle()));
try {
_containerInstance.close();
withListeners(ContainerListener::onStopSuccess);
} catch (final RuntimeException re) {
withListeners(listener -> listener.onStopError(re));
throw re;
} finally {
_containerInstance = null;
try {
_serviceObjects.ungetService(_initializer);
_initializer = null;
}
catch (Throwable t) {
_log.trace(l -> l.trace("CCR Failure in returning initializer instance on {}", bundle(), t));
}
}
}
return true;
}
catch (Throwable t) {
_log.error(l -> l.error("CCR Failure in container bootstrap shutdown on {}", bundle(), t));
return false;
}
}
@Override
public Op closeOp() {
return Op.of(Mode.CLOSE, Type.CONTAINER_BOOTSTRAP, containerState.id());
}
@Override
public boolean open() {
try (Syncro syncro = _lock.open()) {
if (containerState.bundleContext() == null) {
// this bundle was already removed
return false;
}
if (_containerInstance != null) {
return true;
}
if (containerState.containerDTO().components.isEmpty()) {
return false;
}
_log.debug(log -> log.debug("CCR container startup for {}", bundle()));
try {
// always use a new class loader
BundleClassLoader loader = new BundleClassLoader(containerState.bundle(), containerState.extenderBundle());
_initializer = _serviceObjects.getService();
processExtensions(loader, _initializer);
containerState.containerComponentTemplateDTO().properties.forEach(_initializer::addProperty);
_containerInstance = _initializer
.addBeanClasses(containerState.beansModel().getOSGiBeans().stream().map(OSGiBean::getBeanClass).toArray(Class<?>[]::new))
.addBeanXmls(containerState.beansModel().getBeansXml().toArray(new URL[0]))
.setBundleContext(bundle().getBundleContext())
.setClassLoader(loader)
.initialize();
withListeners(ContainerListener::onStartSuccess);
} catch (final RuntimeException re) {
withListeners(listener -> listener.onStartError(re));
throw re;
}
return true;
}
}
@Override
public Op openOp() {
return Op.of(Mode.OPEN, Type.CONTAINER_BOOTSTRAP, containerState.id());
}
protected void processExtensions(BundleClassLoader loader, CDIContainerInitializer initializer) {
AtomicInteger counter = new AtomicInteger();
// Add the internal extensions
initializer.addExtension(
new ConfigurationExtension(containerState),
Maps.of(Constants.SERVICE_ID, counter.decrementAndGet(),
Constants.SERVICE_DESCRIPTION, "Aries CDI ConfigurationExtension"));
initializer.addExtension(
new BundleContextExtension(containerState.bundleContext()),
Maps.of(Constants.SERVICE_ID, counter.decrementAndGet(),
Constants.SERVICE_DESCRIPTION, "Aries CDI BundleContextExtension"));
initializer.addExtension(
new RuntimeExtension(containerState, _configurationBuilder, _singleBuilder, _factoryBuilder),
Maps.of(Constants.SERVICE_ID, counter.decrementAndGet(),
Constants.SERVICE_DESCRIPTION, "Aries CDI RuntimeExtension"));
initializer.addExtension(
new LoggerExtension(containerState),
Maps.of(Constants.SERVICE_ID, counter.decrementAndGet(),
Constants.SERVICE_DESCRIPTION, "Aries CDI LoggerExtension"));
initializer.addExtension(
new ServiceAdapterExtension(),
Maps.of(Constants.SERVICE_ID, counter.decrementAndGet(),
Constants.SERVICE_DESCRIPTION, "Aries CDI ServiceAdapterExtension"));
// Add extensions found from the bundle's class loader, such as those in the Bundle-ClassPath
ServiceLoader.load(Extension.class, containerState.classLoader()).forEach(extension ->
initializer.addExtension(
extension,
Maps.of(Constants.SERVICE_ID, counter.decrementAndGet(),
Constants.SERVICE_DESCRIPTION, "ClassLoader Extension from " + containerState.bundle()))
);
// Add external extensions
for (ExtensionDTO extensionDTO : containerState.containerDTO().extensions) {
ExtendedExtensionDTO extendedExtensionDTO = (ExtendedExtensionDTO)extensionDTO;
Dictionary<String,Object> properties = extendedExtensionDTO.extension.getServiceReference().getProperties();
initializer.addExtension(
extendedExtensionDTO.extension.getService(), Maps.of(properties));
Bundle extensionBundle = extendedExtensionDTO.extension.getServiceReference().getBundle();
getClassesFromExtensionCapability(properties, extensionBundle, initializer);
if (!loader.getBundles().contains(extensionBundle)) {
loader.getBundles().add(extensionBundle);
}
}
}
private void getClassesFromExtensionCapability(Dictionary<String,Object> properties, Bundle extensionBundle, CDIContainerInitializer initializer) {
List<BundleCapability> capabilities = extensionBundle.adapt(BundleWiring.class).getCapabilities(CDI_EXTENSION_PROPERTY);
if (capabilities.isEmpty()) {
return;
}
Map<String, Object> attributes = capabilities.stream().map(Capability::getAttributes).filter(
map -> map.get(CDI_EXTENSION_PROPERTY).equals(properties.get(CDI_EXTENSION_PROPERTY))
).findFirst().orElseGet(Collections::emptyMap);
ofNullable(
attributes.get("aries.cdi.extension.bean.classes")
).map(List.class::cast).map(List<String>::stream).orElseGet(Stream::empty).map(
asFunction(extensionBundle::loadClass)
).forEach(
initializer::addBeanClasses
);
}
private void withListeners(final Consumer<ContainerListener> action) {
final ServiceReference<ContainerListener>[] refs = _listeners.getServiceReferences();
if (refs != null && refs.length > 0) {
final BundleContext bundleContext = bundle().getBundleContext();
Stream.of(refs).forEach(ref -> {
final ContainerListener service = bundleContext.getService(ref);
if (service != null) {
try {
action.accept(service);
} finally {
bundleContext.ungetService(ref);
}
}
});
}
}
private volatile AutoCloseable _containerInstance;
private final ServiceTracker<CDIContainerInitializer, ServiceObjects<CDIContainerInitializer>> _containerTracker;
private final ConfigurationListener.Builder _configurationBuilder;
private final FactoryComponent.Builder _factoryBuilder;
private CDIContainerInitializer _initializer;
private final ServiceObjects<CDIContainerInitializer> _serviceObjects;
private final SingleComponent.Builder _singleBuilder;
private final Syncro _lock = new Syncro(true);
private final Logger _log;
private final ServiceTracker<ContainerListener, ContainerListener> _listeners;
}