| /** |
| * 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 org.apache.aries.cdi.container.internal.util.Filters.asFilter; |
| 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.CDI_CONTAINER_ID; |
| import static org.osgi.service.cdi.CDIConstants.CDI_EXTENSION_PROPERTY; |
| |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Optional; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.ConcurrentHashMap; |
| import java.util.concurrent.CopyOnWriteArrayList; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| import javax.enterprise.inject.spi.BeanManager; |
| |
| import org.apache.aries.cdi.container.internal.ChangeCount; |
| import org.apache.aries.cdi.container.internal.loader.BundleClassLoader; |
| import org.apache.aries.cdi.container.internal.model.BeansModel; |
| import org.apache.aries.cdi.container.internal.model.BeansModelBuilder; |
| import org.apache.aries.cdi.container.internal.model.ExtendedConfigurationTemplateDTO; |
| import org.apache.aries.cdi.container.internal.model.ExtendedExtensionTemplateDTO; |
| import org.apache.aries.cdi.container.internal.util.Logs; |
| import org.apache.aries.cdi.container.internal.util.Throw; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.dto.BundleDTO; |
| import org.osgi.framework.wiring.BundleCapability; |
| import org.osgi.framework.wiring.BundleRequirement; |
| import org.osgi.framework.wiring.BundleWire; |
| import org.osgi.framework.wiring.BundleWiring; |
| import org.osgi.resource.Namespace; |
| import org.osgi.service.cdi.ComponentType; |
| import org.osgi.service.cdi.ConfigurationPolicy; |
| import org.osgi.service.cdi.MaximumCardinality; |
| import org.osgi.service.cdi.runtime.dto.ContainerDTO; |
| import org.osgi.service.cdi.runtime.dto.template.ComponentTemplateDTO; |
| import org.osgi.service.cdi.runtime.dto.template.ContainerTemplateDTO; |
| import org.osgi.service.cm.Configuration; |
| import org.osgi.service.cm.ConfigurationAdmin; |
| import org.osgi.service.log.Logger; |
| import org.osgi.util.promise.Deferred; |
| import org.osgi.util.promise.Promise; |
| import org.osgi.util.promise.PromiseFactory; |
| import org.osgi.util.tracker.ServiceTracker; |
| |
| public class ContainerState { |
| |
| public ContainerState( |
| Bundle bundle, |
| Bundle extenderBundle, |
| ChangeCount ccrChangeCount, |
| PromiseFactory promiseFactory, |
| ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> caTracker, |
| Logs ccrLogs) { |
| |
| _bundle = bundle; |
| _extenderBundle = extenderBundle; |
| _ccrLogs = ccrLogs; |
| _bundleContext = bundle.getBundleContext(); |
| _log = _ccrLogs.getLogger(getClass()); |
| _containerLogs = new Logs.Builder(_bundleContext).build(); |
| |
| _changeCount = new ChangeCount(); |
| _changeCount.addObserver(ccrChangeCount); |
| |
| _promiseFactory = promiseFactory; |
| _caTracker = caTracker; |
| |
| BundleWiring bundleWiring = _bundle.adapt(BundleWiring.class); |
| |
| List<BundleWire> wires = bundleWiring.getRequiredWires(EXTENDER_NAMESPACE); |
| |
| Map<String, Object> cdiAttributes = Collections.emptyMap(); |
| |
| for (BundleWire wire : wires) { |
| BundleCapability capability = wire.getCapability(); |
| Map<String, Object> attributes = capability.getAttributes(); |
| String extender = (String)attributes.get(EXTENDER_NAMESPACE); |
| |
| if (extender.equals(CDI_CAPABILITY_NAME)) { |
| BundleRequirement requirement = wire.getRequirement(); |
| cdiAttributes = requirement.getAttributes(); |
| break; |
| } |
| } |
| |
| _cdiAttributes = Collections.unmodifiableMap(cdiAttributes); |
| |
| wires = bundleWiring.getRequiredWires(CDI_EXTENSION_PROPERTY); |
| |
| List<String> extensionRequirements = new ArrayList<>(); |
| |
| for (BundleWire wire : wires) { |
| String filter = wire.getRequirement().getDirectives().get( |
| Namespace.REQUIREMENT_FILTER_DIRECTIVE); |
| Bundle extensionProvider = wire.getProvider().getBundle(); |
| |
| StringBuilder sb = new StringBuilder(); |
| |
| sb.append("(&"); |
| sb.append(filter); |
| sb.append("("); |
| sb.append(Constants.SERVICE_BUNDLEID); |
| sb.append("="); |
| sb.append(extensionProvider.getBundleId()); |
| sb.append("))"); |
| |
| extensionRequirements.add(sb.toString()); |
| } |
| |
| _containerDTO = new ContainerDTO(); |
| _containerDTO.bundle = _bundle.adapt(BundleDTO.class); |
| _containerDTO.changeCount = _changeCount.get(); |
| _containerDTO.components = new CopyOnWriteArrayList<>(); |
| _containerDTO.errors = new CopyOnWriteArrayList<>(); |
| _containerDTO.extensions = new CopyOnWriteArrayList<>(); |
| _containerDTO.template = new ContainerTemplateDTO(); |
| _containerDTO.template.components = new CopyOnWriteArrayList<>(); |
| _containerDTO.template.extensions = new CopyOnWriteArrayList<>(); |
| _containerDTO.template.id = Optional.ofNullable( |
| (String)_cdiAttributes.get(CDI_CONTAINER_ID) |
| ).orElse( |
| _bundle.getSymbolicName() |
| ); |
| |
| extensionRequirements.forEach( |
| extensionFilter -> { |
| ExtendedExtensionTemplateDTO extensionTemplateDTO = new ExtendedExtensionTemplateDTO(); |
| |
| try { |
| extensionTemplateDTO.filter = asFilter(extensionFilter); |
| extensionTemplateDTO.serviceFilter = extensionFilter; |
| |
| _containerDTO.template.extensions.add(extensionTemplateDTO); |
| } |
| catch (Exception e) { |
| _containerDTO.errors.add(Throw.asString(e)); |
| } |
| } |
| ); |
| |
| _containerComponentTemplateDTO = new ComponentTemplateDTO(); |
| _containerComponentTemplateDTO.activations = new CopyOnWriteArrayList<>(); |
| _containerComponentTemplateDTO.beans = new CopyOnWriteArrayList<>(); |
| _containerComponentTemplateDTO.configurations = new CopyOnWriteArrayList<>(); |
| _containerComponentTemplateDTO.name = _containerDTO.template.id; |
| _containerComponentTemplateDTO.properties = Collections.emptyMap(); |
| _containerComponentTemplateDTO.references = new CopyOnWriteArrayList<>(); |
| _containerComponentTemplateDTO.type = ComponentType.CONTAINER; |
| |
| ExtendedConfigurationTemplateDTO configurationTemplate = new ExtendedConfigurationTemplateDTO(); |
| configurationTemplate.maximumCardinality = MaximumCardinality.ONE; |
| configurationTemplate.pid = Optional.ofNullable( |
| (String)_cdiAttributes.get(CDI_CONTAINER_ID) |
| ).map( |
| s -> s.replaceAll("-", ".") |
| ).orElse( |
| "osgi.cdi." + _bundle.getSymbolicName().replaceAll("-", ".") |
| ); |
| configurationTemplate.policy = ConfigurationPolicy.OPTIONAL; |
| |
| _containerComponentTemplateDTO.configurations.add(configurationTemplate); |
| |
| _containerDTO.template.components.add(_containerComponentTemplateDTO); |
| |
| _aggregateClassLoader = new BundleClassLoader(_bundle, _extenderBundle); |
| |
| _beansModel = new BeansModelBuilder(this, _aggregateClassLoader, bundleWiring, _cdiAttributes).build(); |
| |
| try { |
| new Discovery(this).discover(); |
| } |
| catch (Exception e) { |
| _log.error(l -> l.error("CCR Discovery resulted in errors on {}", bundle, e)); |
| |
| _containerDTO.errors.add(Throw.asString(e)); |
| } |
| |
| _beanManagerDeferred = _promiseFactory.deferred(); |
| } |
| |
| public <T, R> Promise<R> addCallback(CheckedCallback<T, R> checkedCallback) { |
| Deferred<R> deferred = _promiseFactory.deferred(); |
| _callbacks.put(checkedCallback, deferred); |
| return deferred.getPromise(); |
| } |
| |
| public BeanManager beanManager() { |
| try { |
| return _beanManagerDeferred.getPromise().timeout(5000).getValue(); |
| } catch (InvocationTargetException | InterruptedException e) { |
| return Throw.exception(e); |
| } |
| } |
| |
| public void beanManager(BeanManager beanManager) { |
| if (_beanManagerDeferred.getPromise().isDone()) { |
| _beanManagerDeferred = _promiseFactory.deferred(); |
| } |
| _beanManagerDeferred.resolve(beanManager); |
| } |
| |
| public BeansModel beansModel() { |
| return _beansModel; |
| } |
| |
| public Bundle bundle() { |
| return _bundle; |
| } |
| |
| public BundleContext bundleContext() { |
| return _bundleContext; |
| } |
| |
| public ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> caTracker() { |
| return _caTracker; |
| } |
| |
| public Logs ccrLogs() { |
| return _ccrLogs; |
| } |
| |
| public Map<String, Object> cdiAttributes() { |
| return _cdiAttributes; |
| } |
| |
| public BundleClassLoader classLoader() { |
| return _aggregateClassLoader; |
| } |
| |
| public void closing() { |
| try { |
| _closing.set(_promiseFactory.submit(() -> Boolean.TRUE).getValue()); |
| } catch (InvocationTargetException | InterruptedException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| public ComponentContext componentContext() { |
| return _componentContext; |
| } |
| |
| public ComponentTemplateDTO containerComponentTemplateDTO() { |
| return _containerComponentTemplateDTO; |
| } |
| |
| public ContainerDTO containerDTO() { |
| _containerDTO.changeCount = _changeCount.get(); |
| return _containerDTO; |
| } |
| |
| public Logs containerLogs() { |
| return _containerLogs; |
| } |
| |
| public void error(Throwable t) { |
| containerDTO().errors.add(Throw.asString(t)); |
| } |
| |
| public Bundle extenderBundle() { |
| return _extenderBundle; |
| } |
| |
| public Optional<Configuration> findConfig(String pid) { |
| return findConfigs(pid, false).map(arr -> arr[0]); |
| } |
| |
| public Optional<Configuration[]> findConfigs(String pid, boolean factory) { |
| try { |
| String query = "(service.pid=".concat(pid).concat(")"); |
| |
| if (factory) { |
| query = "(service.factoryPid=".concat(pid).concat(")"); |
| } |
| |
| ConfigurationAdmin cm = _caTracker.getService(); |
| |
| if (cm == null) { |
| _log.error(l -> l.error("CCR unexpected error fetching configuration admin for {}", pid)); |
| |
| return Optional.empty(); |
| } |
| |
| return Optional.ofNullable(cm.listConfigurations(query)); |
| } |
| catch (Exception e) { |
| _log.warn(l -> l.warn("CCR unexpected error fetching configuration for {}", pid, e)); |
| |
| return Optional.empty(); |
| } |
| } |
| |
| public String id() { |
| return _containerDTO.template.id; |
| } |
| |
| public void incrementChangeCount() { |
| _changeCount.incrementAndGet(); |
| } |
| |
| public PromiseFactory promiseFactory() { |
| return _promiseFactory; |
| } |
| |
| @SuppressWarnings("unchecked") |
| public <T, R> Promise<T> submit(Op op, Callable<T> task) { |
| try { |
| switch (op.mode) { |
| case CLOSE: { |
| // always perform close synchronously |
| _log.debug(l -> l.debug("CCR submit {}", op)); |
| return _promiseFactory.resolved(task.call()); |
| } |
| case OPEN: |
| // when closing don't do perform any opens |
| // also, don't log it since it's just going to be noise |
| if (_closing.get()) { |
| return _promiseFactory.resolved((T)new Object()); |
| } |
| } |
| } |
| catch (Exception e) { |
| return _promiseFactory.failed(e); |
| } |
| |
| _log.debug(l -> l.debug("CCR submit {}", op)); |
| |
| Promise<T> promise = _promiseFactory.submit(task); |
| |
| for (Entry<CheckedCallback<?, ?>, Deferred<?>> entry : _callbacks.entrySet()) { |
| CheckedCallback<T, R> cc = (CheckedCallback<T, R>)entry.getKey(); |
| if (cc.test(op)) { |
| ((Deferred<R>)entry.getValue()).resolveWith(promise.then(cc, cc)).then( |
| s -> { |
| _callbacks.remove(cc); |
| return s; |
| }, |
| f -> _callbacks.remove(cc) |
| ); |
| } |
| } |
| |
| return promise; |
| } |
| |
| @Override |
| public String toString() { |
| return _bundle.toString(); |
| } |
| |
| private final BundleClassLoader _aggregateClassLoader; |
| private volatile Deferred<BeanManager> _beanManagerDeferred; |
| private final BeansModel _beansModel; |
| private final Bundle _bundle; |
| private final BundleContext _bundleContext; |
| private final Map<CheckedCallback<?, ?>, Deferred<?>> _callbacks = new ConcurrentHashMap<>(); |
| private final ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> _caTracker; |
| private final Logger _log; |
| private final Logs _ccrLogs; |
| private final Map<String, Object> _cdiAttributes; |
| private final ChangeCount _changeCount; |
| private final AtomicBoolean _closing = new AtomicBoolean(false); |
| private final ComponentContext _componentContext = new ComponentContext(); |
| private final ContainerDTO _containerDTO; |
| private final Logs _containerLogs; |
| private final ComponentTemplateDTO _containerComponentTemplateDTO; |
| private final Bundle _extenderBundle; |
| private final PromiseFactory _promiseFactory; |
| |
| } |