/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.component.registration.internal; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.osgi.framework.Bundle; import org.osgi.framework.Constants; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.service.component.ComponentFactory; import org.osgi.service.component.ComponentInstance; import org.osgi.service.packageadmin.PackageAdmin; import de.rcenvironment.core.communication.api.PlatformService; import de.rcenvironment.core.communication.common.LogicalNodeId; import de.rcenvironment.core.component.api.ComponentConstants; import de.rcenvironment.core.component.api.ComponentUtils; import de.rcenvironment.core.component.api.DistributedComponentKnowledgeService; import de.rcenvironment.core.component.api.LoopComponentConstants; import de.rcenvironment.core.component.internal.ComponentBundleConfiguration; import de.rcenvironment.core.component.model.api.ComponentColor; import de.rcenvironment.core.component.model.api.ComponentInstallation; import de.rcenvironment.core.component.model.api.ComponentInterface; import de.rcenvironment.core.component.model.api.ComponentShape; import de.rcenvironment.core.component.model.api.ComponentSize; import de.rcenvironment.core.component.model.api.LazyDisposal; import de.rcenvironment.core.component.model.api.LocalExecutionOnly; import de.rcenvironment.core.component.model.configuration.impl.ConfigurationDefinitionImpl; import de.rcenvironment.core.component.model.configuration.impl.ConfigurationExtensionDefinitionImpl; import de.rcenvironment.core.component.model.endpoint.impl.EndpointDefinitionImpl; import de.rcenvironment.core.component.model.endpoint.impl.EndpointDefinitionsProviderImpl; import de.rcenvironment.core.component.model.endpoint.impl.EndpointGroupDefinitionImpl; import de.rcenvironment.core.component.model.impl.ComponentInstallationImpl; import de.rcenvironment.core.component.model.impl.ComponentInterfaceImpl; import de.rcenvironment.core.component.model.impl.ComponentRevisionImpl; import de.rcenvironment.core.component.registration.api.ComponentRegistry; import de.rcenvironment.core.component.registration.api.Registerable; import de.rcenvironment.core.configuration.CommandLineArguments; import de.rcenvironment.core.configuration.ConfigurationSegment; import de.rcenvironment.core.configuration.ConfigurationService; import de.rcenvironment.core.datamodel.api.EndpointType; import de.rcenvironment.core.toolkitbridge.transitional.ConcurrencyUtils; import de.rcenvironment.core.utils.common.StringUtils; import de.rcenvironment.toolkit.modules.concurrency.api.TaskDescription; /** * Implementation of the {@link ComponentRegistry}. * * @author Roland Gude * @author Jens Ruehmkorf * @author Doreen Seider * @author Heinrich Wendel * @author Robert Mischke * @author Sascha Zur */ public class ComponentRegistryImpl implements ComponentRegistry { private static final int ACTIVATION_TO_COMPONENT_PUBLISHING_DELAY_MSEC = 2000; private static final Log LOGGER = LogFactory.getLog(ComponentRegistryImpl.class); /** * Needed to remove components from {@link ComponentRegistry} which are * added/removed by OSGi dependency injection. */ private final Map<String, String> compFactoryServiceIdToCompInstIdMapping = new HashMap<String, String>(); private final List<ServiceReference<?>> unhandledCompControllers = new ArrayList<ServiceReference<?>>(); private PlatformService platformService; private ConfigurationService configurationService; private PackageAdmin packageAdminService; private LogicalNodeId localNode; private ComponentBundleConfiguration configuration; private org.osgi.service.component.ComponentContext osgiComponentCtx; private DistributedComponentKnowledgeService componentDistributor; private final Map<String, ComponentInstallation> localInstallations = new HashMap<String, ComponentInstallation>(); private final Map<String, ComponentInstallation> publishedInstallations = new HashMap<String, ComponentInstallation>(); private volatile boolean publishDelayPast = false; private final Object installationLock = new Object(); // FIXME Currently, tokens, which were not used, are not removed over time-> // memory leak, but // only minor because of small amount of // unused tokens and because of small size of each token. But anyways: token // garbage collection // must be added -- seid_do, Nov 2013 // (see: https://www.sistec.dlr.de/mantis/view.php?id=9539) private final Set<String> instantiationAuthTokens = Collections.synchronizedSet(new HashSet<String>()); protected synchronized void activate(org.osgi.service.component.ComponentContext newContext) { localNode = platformService.getLocalDefaultLogicalNodeId(); synchronized (unhandledCompControllers) { osgiComponentCtx = newContext; // TODO >6.0.0 rework; bridge code to map the new configuration // layout onto the old java bean ConfigurationSegment configurationSegment = configurationService.getConfigurationSegment("publishing"); try { // simple migration path for now: mapping the whole of // /components onto the old object. // this will need to be changed to expand to more configuration // settings - misc_ro configuration = configurationSegment.mapToObject(ComponentBundleConfiguration.class); } catch (IOException e) { LOGGER.error("Failed to parse component publish settings; using default values", e); configuration = new ComponentBundleConfiguration(); } for (ServiceReference<?> component : unhandledCompControllers) { addComponent(component); } unhandledCompControllers.clear(); } if (!CommandLineArguments.isDoNotStartComponentsRequested()) { Runnable r = new Runnable() { @Override @TaskDescription("Start all bundles providing RCE components") public void run() { // start all future Bundles providing a Component osgiComponentCtx.getBundleContext().addBundleListener(new ComponentBundleListener()); // start all current Bundles providing a Component for (Bundle b : osgiComponentCtx.getBundleContext().getBundles()) { ComponentBundleListener.handleBundle(b); } } }; ConcurrencyUtils.getAsyncTaskService().execute(r); } else { LOGGER.debug("Not triggering start of remaining component bundles as component loading is disabled"); } // start publishing of components after short delay to avoid frequent // updates during initial // component registration phase ConcurrencyUtils.getAsyncTaskService().scheduleAfterDelay(new Runnable() { @Override @TaskDescription("Publish workflow components") public void run() { synchronized (installationLock) { publishDelayPast = true; publishComponents(); } } }, ACTIVATION_TO_COMPONENT_PUBLISHING_DELAY_MSEC); } protected void bindPlatformService(PlatformService newPlatformService) { platformService = newPlatformService; } protected void bindDistributedComponentKnowledgeService(DistributedComponentKnowledgeService newInstance) { componentDistributor = newInstance; } protected void bindConfigurationService(ConfigurationService newConfigurationService) { configurationService = newConfigurationService; } protected void bindPackageAdminService(PackageAdmin newPackageAdminService) { packageAdminService = newPackageAdminService; } /** * Bind method called by the OSGi framework, if a new Component Factory of * type de.rcenvironment.rce.component was registered. The component * registration stuff is done here. */ protected synchronized void addComponent(ServiceReference<?> factoryReference) { if (CommandLineArguments.isDoNotStartComponentsRequested()) { LOGGER.warn("Received a component registration call although components should be disabled: " + factoryReference.getBundle().getSymbolicName()); return; } // if this bundle is not activated yet, store the component controller // and handle it after // activation within the activate method synchronized (unhandledCompControllers) { if (osgiComponentCtx == null) { unhandledCompControllers.add(factoryReference); return; } } // create and thus, register an instance of the component created by the // given factory to // access its service properties // to be able to find the newly created service, create and thus, // register it with an // "unique" id as service property String compIdentifier = UUID.randomUUID().toString(); ComponentFactory factory = (ComponentFactory) osgiComponentCtx.getBundleContext().getService(factoryReference); ComponentInstance compInstance = createOsgiComponentInstance(factory, compIdentifier); if (compInstance != null) { try { ServiceReference<?> componentReference = getComponentReference(compIdentifier); if (componentReference != null) { ComponentRevisionImpl componentRevision = createComponentRevision(componentReference); if (componentRevision != null) { ComponentInstallation componentInstallation = createComponentInstallation(componentRevision); compFactoryServiceIdToCompInstIdMapping.put( factoryReference.getProperty(Constants.SERVICE_ID).toString(), componentInstallation.getInstallationId()); addComponent(componentInstallation); return; } } } finally { try { compInstance.dispose(); } catch (NullPointerException e) { // OSGi complains if no unbind method was declared in // ServiceComponent for a service reference. As we cannot be // sure, // each // component developer is aware of that, a possible NPE is // caught here LOGGER.debug("NPE, most likely cause: unbind method was not declared: " + e.toString()); } } } LOGGER.error(StringUtils.format("Failed to register a component, try restarting RCE (affected bundle: %s)", factoryReference.getBundle().toString())); } protected synchronized void removeComponent(ServiceReference<?> factoryReference) { String compIdentifier = compFactoryServiceIdToCompInstIdMapping .get(factoryReference.getProperty(Constants.SERVICE_ID).toString()); removeComponent(compIdentifier); } private boolean isAllowedToBePublished(ComponentInstallation compInstallation) { ComponentInterface componentInterface = compInstallation.getComponentRevision().getComponentInterface(); if (componentInterface.getLocalExecutionOnly()) { return false; } boolean isPublished = false; if (compInstallation.getIsPublished()) { isPublished = true; } else { // Note: quite inefficient, but not called very often for (String publishedId : configuration.getPublished()) { for (String identifier : componentInterface.getIdentifiers()) { if (identifier.startsWith(publishedId)) { isPublished = true; break; } } } } return isPublished; } private byte[] readIcon(String key, ServiceReference<?> reference) { String iconPath = (String) reference.getProperty(key); if (iconPath != null) { String iconName = (String) reference.getProperty(key); String bundleId = "de.rcenvironment.components."; if (reference.getProperty(ComponentConstants.COMPONENT_ICON_BUNDLE_NAME_KEY) == null) { String componentName = (String) reference.getProperty(ComponentConstants.COMPONENT_NAME_KEY); bundleId += (componentName.toLowerCase() + ".common"); } else { bundleId = (String) reference.getProperty(ComponentConstants.COMPONENT_ICON_BUNDLE_NAME_KEY); } URL url = ComponentUtils.readIconURL(bundleId, iconName); if (url != null) { try (InputStream stream = url.openStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream()) { while (true) { int r = stream.read(); if (r < 0) { break; } bos.write(r); } return bos.toByteArray(); } catch (IOException e) { LOGGER.warn("Failed to read icon: " + iconPath); return null; } } else { LOGGER.warn("Icon not found: " + iconPath); } } return null; } @Override public void addComponent(ComponentInstallation componentInstallation) { final boolean toPublish = isAllowedToBePublished(componentInstallation); synchronized (installationLock) { localInstallations.put(componentInstallation.getInstallationId(), componentInstallation); if (toPublish) { publishedInstallations.put(componentInstallation.getInstallationId(), componentInstallation); } publishComponents(); } LOGGER.debug(StringUtils.format("Registered component: %s (published: %s)", componentInstallation.getComponentRevision() .getComponentInterface().getIdentifier(), toPublish)); } @Override public void removeComponent(String compInstallationId) { synchronized (installationLock) { localInstallations.remove(compInstallationId); publishedInstallations.remove(compInstallationId); publishComponents(); } LOGGER.debug("Removed component: " + compInstallationId); } @Override public void addComponentInstantiationAuthToken(String token) { if (token != null) { instantiationAuthTokens.add(token); } } private void publishComponents() { if (publishDelayPast) { componentDistributor.setLocalComponentInstallations(localInstallations.values(), publishedInstallations.values()); } else { componentDistributor.setLocalComponentInstallations(localInstallations.values(), new ArrayList<ComponentInstallation>()); } } private ComponentInterfaceImpl configureComponentInterfaceFromImplementingClass( ComponentInterfaceImpl componentInterface, String className) { Class<?> componentClass; try { componentClass = Class.forName(className); } catch (ClassNotFoundException e) { LOGGER.error("Failed to load component class: " + className, e); return null; } componentInterface.setLocalExecutionOnly(componentClass.getAnnotation(LocalExecutionOnly.class) != null); componentInterface.setPerformLazyDisposal(componentClass.getAnnotation(LazyDisposal.class) != null); componentInterface.setIsDeprecated( componentClass.getAnnotation(de.rcenvironment.core.component.model.api.Deprecated.class) != null); return componentInterface; } private ComponentInstance createOsgiComponentInstance(ComponentFactory factory, String identifier) { Dictionary<String, String> serviceProperties = new Hashtable<String, String>(); serviceProperties.put(ComponentConstants.COMP_INSTANCE_ID_KEY, identifier); try { return factory.newInstance(serviceProperties); } catch (RuntimeException e) { LOGGER.error( "Failed to load component because of an error in the OSGi DS file or an error in the component's constructor", e); return null; } } private ServiceReference<?> getComponentReference(String identifier) { // search for the previously registered service (component) String filter = "(" + ComponentConstants.COMP_INSTANCE_ID_KEY + "=" + identifier + ")"; ServiceReference<?>[] references; try { references = osgiComponentCtx.getBundleContext().getAllServiceReferences(Registerable.class.getName(), filter); if (references == null || references.length == 0) { LOGGER.error(StringUtils.format( "No component found that provides the service '%s' and that has the temporary identifier '%s'", Registerable.class.getName(), identifier)); return null; } else if (references.length > 1) { LOGGER.warn(StringUtils .format("More than one component found that provides the service '%s' and that has the temporary identifier '%s'," + " first one is taken", Registerable.class.getName(), identifier)); } } catch (InvalidSyntaxException e) { throw new IllegalStateException("Invalid syntax. This is a bug.", e); } return references[0]; } private ComponentInstallation createComponentInstallation(ComponentRevisionImpl componentRevision) { ComponentInstallationImpl componentInstallation = new ComponentInstallationImpl(); // must be changed in future to be unique per node componentInstallation.setInstallationId(componentRevision.getComponentInterface().getIdentifier()); componentInstallation.setComponentRevision(componentRevision); componentInstallation.setNodeIdFromObject(localNode); return componentInstallation; } private void setIdentifiers(ServiceReference<?> componentReference, ComponentInterfaceImpl componentInterface) { String identifier; List<String> identifiers = null; // use explicitly given id(s) if present, otherwise generate id the // "old" way. Object identifierObject = componentReference.getProperty(ComponentConstants.COMPONENT_ID_KEY); if (identifierObject != null) { if (identifierObject instanceof String[]) { String[] identifiersArray = (String[]) identifierObject; if (identifiersArray.length == 0) { throw new IllegalArgumentException( "At least one component identifier must be defined whithin in XML definition file of the component"); } identifier = identifiersArray[0]; identifiers = Arrays.asList(identifiersArray); } else { identifier = (String) identifierObject; } } else { String name = (String) componentReference.getProperty(ComponentConstants.COMPONENT_NAME_KEY); identifier = (String) componentReference.getProperty(ComponentConstants.COMPONENT_CLASS_KEY) + ComponentConstants.COMPONENT_ID_SEPARATOR + name; } if (identifiers == null) { identifiers = new ArrayList<>(); identifiers.add(identifier); } componentInterface.setIdentifier(identifier); componentInterface.setIdentifiers(identifiers); } private ComponentRevisionImpl createComponentRevision(ServiceReference<?> componentReference) { ComponentInterfaceImpl componentInterface = new ComponentInterfaceImpl(); ComponentRevisionImpl componentRevision = new ComponentRevisionImpl(); // read and process all the service properties. actually, they are the // properties of the // component setIdentifiers(componentReference, componentInterface); componentInterface .setDisplayName((String) componentReference.getProperty(ComponentConstants.COMPONENT_NAME_KEY)); componentInterface .setGroupName((String) componentReference.getProperty(ComponentConstants.COMPONENT_NAME_GROUP)); componentInterface.setVersion((String) componentReference.getProperty(ComponentConstants.VERSION_DEF_KEY)); componentInterface.setIcon16(readIcon(ComponentConstants.ICON_16_KEY, componentReference)); componentInterface.setIcon24(readIcon(ComponentConstants.ICON_24_KEY, componentReference)); componentInterface.setIcon32(readIcon(ComponentConstants.ICON_32_KEY, componentReference)); componentInterface.setSize(getComponentSize(componentReference)); componentInterface.setColor(getComponentColor(componentReference)); componentInterface.setShape(getComponentShape(componentReference)); componentInterface.setCanHandleNotAValueDataTypes(getCanHandleNotAValueDataTypes(componentReference)); componentInterface.setIsLoopDriver(getIsResetSink(componentReference)); try { componentInterface .setInputDefinitionsProvider(createEndpointDefinitionsProvider(ComponentConstants.INPUTS_DEF_KEY, componentReference, componentReference.getBundle(), EndpointType.INPUT)); } catch (IOException e) { LOGGER.error("Failed to parse input definition", e); return null; } try { componentInterface .setOutputDefinitionsProvider(createEndpointDefinitionsProvider(ComponentConstants.OUTPUTS_DEF_KEY, componentReference, componentReference.getBundle(), EndpointType.OUTPUT)); } catch (IOException e) { LOGGER.error("Failed to parse output definition", e); return null; } try { componentInterface.setConfigurationDefinition( createConfigurationDefinition(ComponentConstants.CONFIGURATION_DEF_KEY, componentReference)); } catch (IOException e) { LOGGER.error("Failed to parse configuration definition", e); return null; } try { componentInterface.setConfigurationExtensionDefinitions( createConfigurationExtensionDefinitions(componentReference.getBundle())); } catch (IOException e) { LOGGER.error("Failed to parse extension definition", e); return null; } String className = (String) componentReference.getProperty(ComponentConstants.COMPONENT_CLASS_KEY); configureComponentInterfaceFromImplementingClass(componentInterface, className); componentRevision.setComponentInterface(componentInterface); componentRevision.setClassName(className); return componentRevision; } private ComponentColor getComponentColor(ServiceReference<?> componentReference) { String color = (String) componentReference.getProperty(ComponentConstants.COMPONENT_COLOR_KEY); if (color != null) { try { return ComponentColor.valueOf(color.toUpperCase()); } catch (IllegalArgumentException e) { LOGGER.error(StringUtils.format( "Color declared under '%s' is invalid: %s. Valid ones are: %s. Default will be used: %s", ComponentConstants.COMPONENT_COLOR_KEY, color, Arrays.toString(ComponentColor.values()), ComponentConstants.COMPONENT_COLOR_STANDARD)); } } return ComponentConstants.COMPONENT_COLOR_STANDARD; } private boolean getCanHandleNotAValueDataTypes(ServiceReference<?> componentReference) { return Boolean.valueOf( (String) componentReference.getProperty(ComponentConstants.COMPONENT_CAN_HANDLE_NAV_INPUT_DATA_TYPES)); } private boolean getIsResetSink(ServiceReference<?> componentReference) { return Boolean.valueOf((String) componentReference.getProperty(LoopComponentConstants.COMPONENT_IS_RESET_SINK)); } private ComponentSize getComponentSize(ServiceReference<?> componentReference) { String size = (String) componentReference.getProperty(ComponentConstants.COMPONENT_SIZE_KEY); if (size != null) { try { return ComponentSize.valueOf(size.toUpperCase()); } catch (IllegalArgumentException e) { LOGGER.error(StringUtils.format( "Size declared under '%s' is not valid: %s. Valid ones are: %s. Default will be used: %s", ComponentConstants.COMPONENT_SIZE_KEY, size, Arrays.toString(ComponentSize.values()), ComponentConstants.COMPONENT_SIZE_STANDARD)); } } return ComponentConstants.COMPONENT_SIZE_STANDARD; } private ComponentShape getComponentShape(ServiceReference<?> componentReference) { String shape = (String) componentReference.getProperty(ComponentConstants.COMPONENT_SHAPE_KEY); if (shape != null) { try { return ComponentShape.valueOf(shape.toUpperCase()); } catch (IllegalArgumentException e) { LOGGER.error(StringUtils.format( "Shape declared under '%s' is not valid: %s. Valid ones are: %s. Default will be used: %s", ComponentConstants.COMPONENT_SHAPE_KEY, shape, Arrays.toString(ComponentShape.values()), ComponentConstants.COMPONENT_SHAPE_STANDARD)); } } return ComponentConstants.COMPONENT_SHAPE_STANDARD; } private EndpointDefinitionsProviderImpl createEndpointDefinitionsProvider(String key, ServiceReference<?> reference, Bundle componentsBundle, EndpointType type) throws IOException { String file = (String) reference.getProperty(key); if (file == null) { return new EndpointDefinitionsProviderImpl(); } URL fileUrl = reference.getBundle().getResource(file); if (fileUrl == null) { throw new IOException("Endpoint definition file doesn't exist: " + file); } List<InputStream> extendedStaticInputStreams = getInputStreamsForEndpointMetaDataExtensions(reference, componentsBundle, type); List<InputStream> extendedDynamicInputStreams = getInputStreamsForEndpointMetaDataExtensions(reference, componentsBundle, type); EndpointDefinitionsProviderImpl endpointProvider = new EndpointDefinitionsProviderImpl(); Set<EndpointDefinitionImpl> endpointDefinitions; try (InputStream staticEndpointDescriptionInputStream = fileUrl.openStream(); InputStream dynamicEndpointDescriptionInputStream = fileUrl.openStream()) { endpointDefinitions = ComponentUtils.extractStaticEndpointDefinition(staticEndpointDescriptionInputStream, extendedStaticInputStreams, type); endpointDefinitions.addAll(ComponentUtils.extractDynamicEndpointDefinition( dynamicEndpointDescriptionInputStream, extendedDynamicInputStreams, type)); } catch (IOException e) { throw new IOException("Failed to parse endpoint definition file: " + file, e); } endpointProvider.setEndpointDefinitions(endpointDefinitions); Set<EndpointGroupDefinitionImpl> inputGroupDefinitions; try (InputStream endpointGroupDefinitionInputStream = fileUrl.openStream(); InputStream staticEndpointGroupDefinitionInputStream = fileUrl.openStream(); InputStream dynamicEndpointGroupDefinitionInputStream = fileUrl.openStream()) { inputGroupDefinitions = ComponentUtils .extractStaticInputGroupDefinitions(staticEndpointGroupDefinitionInputStream); inputGroupDefinitions.addAll( ComponentUtils.extractDynamicInputGroupDefinitions(dynamicEndpointGroupDefinitionInputStream)); } catch (IOException e) { throw new IOException("Failed to parse endpoint group definition file: " + file, e); } endpointProvider.setEndpointGroupDefinitions(inputGroupDefinitions); return endpointProvider; } private List<InputStream> getInputStreamsForEndpointMetaDataExtensions(ServiceReference<?> reference, Bundle componentsBundle, EndpointType type) throws IOException { List<InputStream> inputStreams = new ArrayList<>(); String manifestHeaderKey; if (type == EndpointType.INPUT) { manifestHeaderKey = ComponentConstants.MANIFEST_ENTRY_RCE_COMPONENT_EXTENSION_INPUT_META_DATA; } else { manifestHeaderKey = ComponentConstants.MANIFEST_ENTRY_RCE_COMPONENT_EXTENSION_OUTPUT_META_DATA; } for (Bundle fragment : getFragmentBundlesProvidingPropertyExtensions(componentsBundle)) { Dictionary<String, String> manifestHeaders = fragment.getHeaders(); String extensionFile = manifestHeaders.get(manifestHeaderKey); if (extensionFile != null) { URL fileUrl = reference.getBundle().getResource(extensionFile); if (fileUrl == null) { throw new IOException("Endpoint definition file doesn't exist: " + extensionFile); } inputStreams.add(fileUrl.openStream()); } } return inputStreams; } private ConfigurationDefinitionImpl createConfigurationDefinition(String key, ServiceReference<?> reference) throws IOException { String file = (String) reference.getProperty(key); if (file == null) { return new ConfigurationDefinitionImpl(); } URL fileUrl = reference.getBundle().getResource(file); if (fileUrl == null) { throw new IOException("Configuration definition file doesn't exist: " + file); } try (InputStream configurationDescriptionInputStream = fileUrl.openStream(); InputStream placeholdersDescriptionInputStream = fileUrl.openStream(); InputStream activationFilterDescriptionInputStream = fileUrl.openStream()) { return ComponentUtils.extractConfigurationDescription(configurationDescriptionInputStream, placeholdersDescriptionInputStream, activationFilterDescriptionInputStream); } catch (IOException e) { throw new IOException("Failed to parse configuration definition", e); } } private Set<ConfigurationExtensionDefinitionImpl> createConfigurationExtensionDefinitions(Bundle componentsBundle) throws IOException { Set<ConfigurationExtensionDefinitionImpl> descs = new HashSet<ConfigurationExtensionDefinitionImpl>(); for (Bundle fragment : getFragmentBundlesProvidingPropertyExtensions(componentsBundle)) { Dictionary<String, String> manifestHeaders = fragment.getHeaders(); String extConfigFile = manifestHeaders .get(ComponentConstants.MANIFEST_ENTRY_RCE_COMPONENT_EXTENSION_CONFIGURATION); if (extConfigFile != null) { descs.add(createConfigurationExtensionDefinition(componentsBundle, extConfigFile)); } } return descs; } private ConfigurationExtensionDefinitionImpl createConfigurationExtensionDefinition(Bundle bundle, String file) throws IOException { URL fileUrl = bundle.getResource(file); if (fileUrl == null) { throw new IOException("Configuration extension file doesn't exist: " + file); } try (InputStream configurationDescriptionInputStream = fileUrl.openStream(); InputStream placeholdersDescriptionInputStream = fileUrl.openStream(); InputStream activationFilterDescriptionInputStream = fileUrl.openStream();) { return ComponentUtils.extractConfigurationExtensionDescription(configurationDescriptionInputStream, placeholdersDescriptionInputStream, activationFilterDescriptionInputStream); } catch (IOException e) { throw new IOException("Failed to parse configuration extension definition file: " + file, e); } } private Bundle[] getFragmentBundlesProvidingPropertyExtensions(Bundle componentsBundle) { Bundle[] fragments = packageAdminService.getFragments(componentsBundle); if (fragments == null) { fragments = new Bundle[0]; } return fragments; } /** * For test purposes only. */ protected void setOsgiComponentContext(org.osgi.service.component.ComponentContext compContext) { this.osgiComponentCtx = compContext; } /** * For test pruposes only. */ protected void setComponentBundleConfiguration(ComponentBundleConfiguration config) { this.configuration = config; } }