/*
* Copyright 2016 Guillaume Nodet
*
* 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.ops4j.pax.cdi.extension.impl.osgi;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.felix.scr.impl.manager.ComponentHolder;
import org.apache.felix.scr.impl.manager.ComponentManager;
import org.apache.felix.scr.impl.manager.ReferenceManager;
import org.apache.felix.scr.impl.metadata.ComponentMetadata;
import org.apache.felix.scr.impl.metadata.ReferenceMetadata;
import org.ops4j.pax.cdi.extension.api.runtime.CdiOsgiRuntime;
import org.ops4j.pax.cdi.extension.api.runtime.dto.ComponentConfigurationDTO;
import org.ops4j.pax.cdi.extension.api.runtime.dto.ComponentDescriptionDTO;
import org.ops4j.pax.cdi.extension.api.runtime.dto.ReferenceDTO;
import org.ops4j.pax.cdi.extension.api.runtime.dto.SatisfiedReferenceDTO;
import org.ops4j.pax.cdi.extension.api.runtime.dto.UnsatisfiedReferenceDTO;
import org.ops4j.pax.cdi.extension.impl.component2.ComponentRegistry;
import org.osgi.dto.DTO;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.dto.BundleDTO;
import org.osgi.framework.dto.ServiceReferenceDTO;
public class Registry implements CdiOsgiRuntime {
private static final String[] EMPTY = {};
private static final Registry INSTANCE = new Registry();
public static Registry getInstance() {
return INSTANCE;
}
private final List<ComponentRegistry> registries = new CopyOnWriteArrayList<>();
private Registry() {
}
public void register(ComponentRegistry componentRegistry) {
if (!registries.contains(componentRegistry)) {
registries.add(componentRegistry);
}
}
public void unregister(ComponentRegistry componentRegistry) {
registries.remove(componentRegistry);
}
@Override
public Collection<ComponentDescriptionDTO> getComponentDescriptionDTOs(Bundle... bundles) {
Collection<ComponentDescriptionDTO> dtos = new ArrayList<>();
for (ComponentRegistry registry : registries) {
Bundle bundle = registry.getBundleContext().getBundle();
if (bundles.length == 0 || Arrays.asList(bundles).contains(bundle)) {
for (ComponentHolder<?> holder : registry.getComponentHolders()) {
dtos.add(holderToDescription(holder));
}
}
}
return dtos;
}
@Override
public ComponentDescriptionDTO getComponentDescriptionDTO(Bundle bundle, String name) {
return getComponentDescriptionDTO(bundle.getBundleId(), name);
}
@Override
public Collection<ComponentConfigurationDTO> getComponentConfigurationDTOs(ComponentDescriptionDTO description) {
if (description == null) {
return Collections.emptyList();
}
ComponentHolder<?> holder = getHolderFromDescription(description);
if (holder == null) {
return Collections.emptyList();
}
description = holderToDescription(holder);
List<? extends ComponentManager<?>> managers = holder.getComponents();
List<ComponentConfigurationDTO> result = new ArrayList<>(managers.size());
for (ComponentManager<?> manager : managers) {
result.add(managerToConfiguration(manager, description));
}
return result;
}
private ComponentHolder<?> getHolderFromDescription(ComponentDescriptionDTO description) {
if (description.bundle == null) {
throw new IllegalArgumentException("No bundle supplied in ComponentDescriptionDTO named " + description.name);
}
long bundleId = description.bundle.id;
for (ComponentRegistry registry : registries) {
if (registry.getBundleContext().getBundle().getBundleId() == bundleId) {
return registry.getComponentHolder(description.name);
}
}
return null;
}
private ComponentDescriptionDTO getComponentDescriptionDTO(long bundleId, String name) {
for (ComponentRegistry registry : registries) {
if (registry.getBundleContext().getBundle().getBundleId() == bundleId) {
ComponentHolder<?> holder = registry.getComponentHolder(name);
if (holder != null) {
return holderToDescription(holder);
}
}
}
return null;
}
private ComponentDescriptionDTO holderToDescription(ComponentHolder<?> holder) {
ComponentDescriptionDTO dto = new ComponentDescriptionDTO();
ComponentMetadata m = holder.getComponentMetadata();
dto.activate = m.getActivate();
dto.bundle = bundleToDTO(holder.getActivator().getBundleContext());
dto.configurationPid = m.getConfigurationPid().toArray(new String[m.getConfigurationPid().size()]);
dto.configurationPolicy = m.getConfigurationPolicy();
dto.deactivate = m.getDeactivate();
dto.defaultEnabled = m.isEnabled();
dto.factory = m.getFactoryIdentifier();
dto.immediate = m.isImmediate();
dto.implementationClass = m.getImplementationClassName();
dto.modified = m.getModified();
dto.name = m.getName();
dto.properties = deepCopy(m.getProperties());
dto.references = refsToDTO(m.getDependencies());
dto.scope = m.getServiceMetadata() == null ? null : m.getServiceMetadata().getScope().name();
dto.serviceInterfaces = m.getServiceMetadata() == null ? EMPTY : m.getServiceMetadata().getProvides();
return dto;
}
private Map<String, Object> deepCopy(Map<String, Object> source) {
HashMap<String, Object> result = new HashMap<>(source.size());
for (Map.Entry<String, Object> entry : source.entrySet()) {
result.put(entry.getKey(), convert(entry.getValue()));
}
return result;
}
private Map<String, Object> deepCopy(ServiceReference<?> source) {
String[] keys = source.getPropertyKeys();
HashMap<String, Object> result = new HashMap<String, Object>(keys.length);
for (String key : keys) {
result.put(key, convert(source.getProperty(key)));
}
return result;
}
Object convert(Object source) {
if (source.getClass().isArray()) {
Class<?> type = source.getClass().getComponentType();
if (checkType(type)) {
return source;
}
return String.valueOf(source);
/* array copy code in case it turns out to be needed
int length = Array.getLength(source);
Object copy = Array.newInstance(type, length);
for (int i = 0; i<length; i++)
{
Array.set(copy, i, Array.get(source, i));
}
return copy;
*/
}
if (checkType(source.getClass())) {
return source;
}
return String.valueOf(source);
}
boolean checkType(Class<?> type) {
if (type == String.class) return true;
if (type == Boolean.class) return true;
if (Number.class.isAssignableFrom(type)) return true;
if (DTO.class.isAssignableFrom(type)) return true;
return false;
}
private ReferenceDTO[] refsToDTO(List<ReferenceMetadata> dependencies) {
ReferenceDTO[] dtos = new ReferenceDTO[dependencies.size()];
int i = 0;
for (ReferenceMetadata r : dependencies) {
ReferenceDTO dto = new ReferenceDTO();
dto.bind = r.getBind();
dto.cardinality = r.getCardinality();
dto.field = r.getField();
dto.fieldOption = r.getFieldOption();
dto.interfaceName = r.getInterface();
dto.name = r.getName();
dto.policy = r.getPolicy();
dto.policyOption = r.getPolicyOption();
dto.scope = r.getScope().name();
dto.target = r.getTarget();
dto.unbind = r.getUnbind();
dto.updated = r.getUpdated();
dtos[i++] = dto;
}
return dtos;
}
private BundleDTO bundleToDTO(BundleContext bundleContext) {
if (bundleContext == null) {
return null;
}
Bundle bundle = bundleContext.getBundle();
if (bundle == null) {
return null;
}
BundleDTO b = new BundleDTO();
b.id = bundle.getBundleId();
b.lastModified = bundle.getLastModified();
b.state = bundle.getState();
b.symbolicName = bundle.getSymbolicName();
b.version = bundle.getVersion().toString();
return b;
}
private ComponentConfigurationDTO managerToConfiguration(ComponentManager<?> manager, ComponentDescriptionDTO description) {
ComponentConfigurationDTO dto = new ComponentConfigurationDTO();
dto.satisfiedReferences = satisfiedRefManagersToDTO(manager.getReferenceManagers());
dto.unsatisfiedReferences = unsatisfiedRefManagersToDTO(manager.getReferenceManagers());
dto.description = description;
dto.id = manager.getId();
dto.properties = new HashMap<>(manager.getProperties()); // TODO deep copy?
dto.state = manager.getSpecState();
return dto;
}
private SatisfiedReferenceDTO[] satisfiedRefManagersToDTO(List<? extends ReferenceManager<?, ?>> referenceManagers) {
List<SatisfiedReferenceDTO> dtos = new ArrayList<>();
for (ReferenceManager<?, ?> ref : referenceManagers) {
if (ref.isSatisfied()) {
SatisfiedReferenceDTO dto = new SatisfiedReferenceDTO();
dto.name = ref.getName();
dto.target = ref.getTarget();
List<ServiceReference<?>> serviceRefs = ref.getServiceReferences();
ServiceReferenceDTO[] srDTOs = new ServiceReferenceDTO[serviceRefs.size()];
int j = 0;
for (ServiceReference<?> serviceRef : serviceRefs) {
ServiceReferenceDTO srefDTO = serviceReferenceToDTO(serviceRef);
if (srefDTO != null)
srDTOs[j++] = srefDTO;
}
dto.boundServices = srDTOs;
dtos.add(dto);
}
}
return dtos.toArray(new SatisfiedReferenceDTO[dtos.size()]);
}
private UnsatisfiedReferenceDTO[] unsatisfiedRefManagersToDTO(List<? extends ReferenceManager<?, ?>> referenceManagers) {
List<UnsatisfiedReferenceDTO> dtos = new ArrayList<UnsatisfiedReferenceDTO>();
for (ReferenceManager<?, ?> ref : referenceManagers) {
if (!ref.isSatisfied()) {
UnsatisfiedReferenceDTO dto = new UnsatisfiedReferenceDTO();
dto.name = ref.getName();
dto.target = ref.getTarget();
List<ServiceReference<?>> serviceRefs = ref.getServiceReferences();
ServiceReferenceDTO[] srDTOs = new ServiceReferenceDTO[serviceRefs.size()];
int j = 0;
for (ServiceReference<?> serviceRef : serviceRefs) {
ServiceReferenceDTO srefDTO = serviceReferenceToDTO(serviceRef);
if (srefDTO != null)
srDTOs[j++] = srefDTO;
}
dto.targetServices = srDTOs;
dtos.add(dto);
}
}
return dtos.toArray(new UnsatisfiedReferenceDTO[dtos.size()]);
}
private ServiceReferenceDTO serviceReferenceToDTO(ServiceReference<?> serviceRef) {
if (serviceRef == null)
return null;
ServiceReferenceDTO dto = new ServiceReferenceDTO();
Bundle bundle = serviceRef.getBundle();
// No bundle ever has -1 as ID, so this indicates no bundle.
dto.bundle = bundle != null ? bundle.getBundleId() : -1;
dto.id = (Long) serviceRef.getProperty(Constants.SERVICE_ID);
dto.properties = deepCopy(serviceRef);
Bundle[] usingBundles = serviceRef.getUsingBundles();
if (usingBundles != null) {
long[] usingBundleIds = new long[usingBundles.length];
for (int i = 0; i < usingBundles.length; i++) {
usingBundleIds[i] = usingBundles[i].getBundleId();
}
dto.usingBundles = usingBundleIds;
}
return dto;
}
}