blob: 1bf268a8eaa2a909a1a654701d6620c12753bd47 [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.model;
import static org.apache.aries.cdi.container.internal.util.Filters.asFilter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.aries.cdi.container.internal.container.ContainerState;
import org.apache.aries.cdi.container.internal.container.Op;
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.container.ReferenceSync;
import org.apache.aries.cdi.container.internal.util.Conversions;
import org.apache.aries.cdi.container.internal.util.Syncro;
import org.osgi.framework.Constants;
import org.osgi.service.cdi.ConfigurationPolicy;
import org.osgi.service.cdi.annotations.Reference;
import org.osgi.service.cdi.runtime.dto.ComponentDTO;
import org.osgi.service.cdi.runtime.dto.ComponentInstanceDTO;
import org.osgi.service.cdi.runtime.dto.ConfigurationDTO;
import org.osgi.service.cdi.runtime.dto.ReferenceDTO;
import org.osgi.service.cdi.runtime.dto.template.ComponentTemplateDTO;
import org.osgi.service.cdi.runtime.dto.template.ConfigurationTemplateDTO;
import org.osgi.service.cdi.runtime.dto.template.ReferenceTemplateDTO;
import org.osgi.service.log.Logger;
import org.osgi.util.tracker.ServiceTracker;
public class ExtendedComponentInstanceDTO extends ComponentInstanceDTO {
public boolean active;
public String pid;
public ComponentTemplateDTO template;
private final InstanceActivator.Builder<?> _builder;
private final Long _componentId = _componentIds.incrementAndGet();
private final ContainerState _containerState;
private final Logger _log;
private final AtomicReference<InstanceActivator> _noRequiredDependenciesActivator = new AtomicReference<>();
private final Syncro sync = new Syncro(true);
public ExtendedComponentInstanceDTO(
ContainerState containerState,
InstanceActivator.Builder<?> builder) {
_containerState = containerState;
_builder = builder;
_log = _containerState.containerLogs().getLogger(getClass());
}
public boolean close() {
try (Syncro open = sync.open()) {
_containerState.submit(Op.of(Mode.CLOSE, Type.REFERENCES, ident()),
() -> {
references.removeIf(
r -> {
ExtendedReferenceDTO referenceDTO = (ExtendedReferenceDTO)r;
referenceDTO.serviceTracker.close();
return true;
}
);
if (_noRequiredDependenciesActivator.get() != null) {
_containerState.submit(
_noRequiredDependenciesActivator.get().closeOp(),
() -> _noRequiredDependenciesActivator.get().close()
).onFailure(
f -> {
_log.error(l -> l.error("CCR Error in CLOSE on {}", ident(), f));
_containerState.error(f);
}
);
}
return true;
}
).onFailure(
f -> {
_log.error(l -> l.error("CCR Error in component instance stop on {}", this, f));
}
);
properties = null;
return true;
}
}
public Op closeOp() {
return Op.of(Mode.CLOSE, getType(), ident());
}
/**
* @return true when all the configuration templates are resolved, otherwise false
*/
public final boolean configurationsResolved() {
for (ConfigurationTemplateDTO template : template.configurations) {
if (template.policy == ConfigurationPolicy.REQUIRED) {
// find a configuration snapshot or not resolved
boolean found = false;
for (ConfigurationDTO snapshot : configurations) {
if (snapshot.template == template) {
found = true;
}
}
if (!found) {
return false;
}
}
}
return true;
}
public final boolean referencesResolved() {
for (ReferenceTemplateDTO template : template.references) {
if (template.minimumCardinality > 0) {
// find a reference snapshot or not resolved
boolean found = false;
for (ReferenceDTO snapshot : references) {
if (!snapshot.template.equals(template)) continue;
ExtendedReferenceDTO extended = (ExtendedReferenceDTO)snapshot;
if (extended.matches.size() >= extended.minimumCardinality) {
found = true;
}
}
if (!found) {
return false;
}
}
}
return true;
}
public boolean open() {
try (Syncro open = sync.open()) {
if (!configurationsResolved() || (properties != null)) {
return false;
}
ConfigurationDTO containerConfiguration = containerConfiguration();
if (containerConfiguration != null) {
Boolean enabled = Conversions.convert(
containerConfiguration.properties.get(
template.name.concat(".enabled"))
).defaultValue(Boolean.TRUE).to(Boolean.class);
if (!enabled) {
_containerState.containerDTO().components.stream().filter(
c -> c.template == template
).forEach(c -> c.enabled = false);
return false;
}
else {
_containerState.containerDTO().components.stream().filter(
c -> c.template == template
).forEach(c -> c.enabled = true);
}
}
properties = componentProperties(null);
template.references.stream().map(ExtendedReferenceTemplateDTO.class::cast).forEach(
t -> {
ExtendedReferenceDTO referenceDTO = new ExtendedReferenceDTO();
if (t.collectionType == CollectionType.BINDER_SERVICE) {
referenceDTO.binder = new BindServiceImpl<>(_containerState);
}
else if (t.collectionType == CollectionType.BINDER_REFERENCE) {
referenceDTO.binder = new BindServiceReferenceImpl<>(_containerState);
}
else if (t.collectionType == CollectionType.BINDER_BEAN_SERVICE_OBJECTS) {
referenceDTO.binder = new BindBeanServiceObjectsImpl<>(_containerState);
}
referenceDTO.matches = new CopyOnWriteArrayList<>();
referenceDTO.minimumCardinality = minimumCardinality(t.name, t.minimumCardinality);
referenceDTO.targetFilter = targetFilter(t.serviceType, t.name, t.targetFilter);
referenceDTO.template = t;
referenceDTO.serviceTracker = new ServiceTracker<>(
_containerState.bundleContext(),
asFilter(referenceDTO.targetFilter),
new ReferenceSync(_containerState, referenceDTO, this, _builder));
references.add(referenceDTO);
}
);
_containerState.submit(
Op.of(Mode.OPEN, Type.REFERENCES, ident()),
() -> {
references.stream().map(ExtendedReferenceDTO.class::cast).forEach(
r -> r.serviceTracker.open()
);
return referencesResolved();
}
).then(
s -> {
if (s.getValue()) {
// none of the reference dependencies are required
_noRequiredDependenciesActivator.set(_builder.setInstance(this).build());
return _containerState.submit(
_noRequiredDependenciesActivator.get().openOp(),
() -> _noRequiredDependenciesActivator.get().open()
).onFailure(
f -> {
_log.error(l -> l.error("CCR Error in OPEN on {}", ident(), f));
_containerState.error(f);
}
);
}
return s;
}
);
return true;
}
}
private ConfigurationDTO containerConfiguration() {
List<ComponentDTO> components = _containerState.containerDTO().components;
if (components.isEmpty()) {
return null;
}
List<ComponentInstanceDTO> instances = components.get(0).instances;
if (instances.isEmpty()) {
return null;
}
List<ConfigurationDTO> configurations = instances.get(0).configurations;
if (configurations.isEmpty()) {
return null;
}
return configurations.get(0);
}
public Op openOp() {
return Op.of(Mode.OPEN, getType(), ident());
}
public Map<String, Object> componentProperties(Map<String, Object> others) {
_log.debug(l -> l.debug("ComponentProperties: merging"));
Map<String, Object> props = new HashMap<>();
if (others != null) {
props.putAll(others);
_log.debug(l -> l.debug("ComponentProperties: others {}", props));
}
props.putAll(template.properties);
_log.debug(l -> l.debug("ComponentProperties: template {}", props));
List<String> servicePids = new ArrayList<>();
for (ConfigurationTemplateDTO t : template.configurations) {
configurations.stream().filter(
c -> c.template.equals(t)
).findFirst().ifPresent(
c -> {
Map<String, Object> copy = new HashMap<>(c.properties);
Optional.ofNullable(
copy.remove(Constants.SERVICE_PID)
).map(String.class::cast).ifPresent(
v -> servicePids.add(v)
);
props.putAll(copy);
_log.debug(l -> l.debug("ComponentProperties: config {}:{}:{} {}", t.pid, t.policy, t.maximumCardinality, props));
}
);
}
if (!servicePids.isEmpty()) {
props.put(Constants.SERVICE_PID, servicePids);
}
props.put("component.id", _componentId);
props.put("component.name", template.name);
_log.debug(l -> l.debug("ComponentProperties: final {}", props));
return props;
}
private Type getType() {
switch (template.type) {
case SINGLE: return Type.SINGLE_INSTANCE;
case FACTORY: return Type.FACTORY_INSTANCE;
default: return Type.CONTAINER_INSTANCE;
}
}
private int minimumCardinality(String componentName, int minimumCardinality) {
Objects.requireNonNull(properties);
Objects.requireNonNull(componentName);
return Optional.ofNullable(
properties.get(componentName.concat(".cardinality.minimum"))
).map(
v -> Integer.valueOf(String.valueOf(v))
).filter(
v -> v >= minimumCardinality
).orElse(minimumCardinality);
}
private String targetFilter(String serviceType, String componentName, String targetFilter) {
Objects.requireNonNull(properties);
Objects.requireNonNull(serviceType);
Objects.requireNonNull(componentName);
Objects.requireNonNull(targetFilter);
String base = Reference.Any.class.getName().equals(serviceType) || Object.class.getName().equals(serviceType) ?
"" : "(objectClass=".concat(serviceType).concat(")");
String extraFilter = Optional.ofNullable(
properties.get(componentName.concat(".target"))
).map(
v -> v + targetFilter
).orElse(targetFilter);
if (extraFilter.length() == 0) {
return base;
}
return "(&".concat(base).concat(extraFilter).concat(")");
}
public String ident() {
return template.name + "[" + _componentId + "]";
}
private static final AtomicLong _componentIds = new AtomicLong();
}