This is an automated email from the ASF dual-hosted git repository. tjwatson pushed a commit to branch scrR8 in repository https://gitbox.apache.org/repos/asf/felix-dev.git
commit 5d167e215e240ae5a779023c0f7e6b24f9c6b425 Author: Thomas Watson <[email protected]> AuthorDate: Tue Jan 12 12:24:45 2021 -0600 Add support for Optional<ServiceType> to be injected This is only supported for unary (0..1, 1..1) cardinality injection to fields and constructor. Method injection is not supported for Optional type injection. --- .../apache/felix/scr/impl/inject/ValueUtils.java | 14 +- .../felix/scr/impl/inject/field/FieldHandler.java | 45 ++++-- .../felix/scr/impl/inject/internal/ClassUtils.java | 3 + .../inject/internal/ComponentConstructorImpl.java | 10 +- .../scr/integration/ComponentOptionalTest.java | 170 +++++++++++++++++++++ .../components/ConstructorSingleReference.java | 12 ++ .../components/InjectOptionalComponent.java | 162 ++++++++++++++++++++ .../resources/integration_test_inject_optional.xml | 121 +++++++++++++++ 8 files changed, 523 insertions(+), 14 deletions(-) diff --git a/scr/src/main/java/org/apache/felix/scr/impl/inject/ValueUtils.java b/scr/src/main/java/org/apache/felix/scr/impl/inject/ValueUtils.java index 2e29a3c..4f2af5c 100644 --- a/scr/src/main/java/org/apache/felix/scr/impl/inject/ValueUtils.java +++ b/scr/src/main/java/org/apache/felix/scr/impl/inject/ValueUtils.java @@ -23,6 +23,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Map; +import java.util.Optional; import org.apache.felix.scr.impl.helper.ReadOnlyDictionary; import org.apache.felix.scr.impl.inject.internal.Annotations; @@ -54,7 +55,8 @@ public class ValueUtils { ref_serviceObjects, // reference (field, constructor, method) ref_serviceType, // reference (field, constructor, method) ref_map, // reference (field, constructor, method) - ref_tuple // reference (field, constructor ??) // TDODO + ref_tuple, // reference (field, constructor ??) // TDODO + ref_optional // reference (field, constructor, XX) } /** Empty array. */ @@ -148,6 +150,14 @@ public class ValueUtils { { return ValueType.ref_formatterLogger; } + // 1.5 Optional + else if (typeClass == ClassUtils.OPTIONAL_CLASS) + { + // Note that the first check for "typeClass.isAssignableFrom(referenceType)" + // will handle case where they want an actual Optional service type. + + valueType = ValueType.ref_optional; + } else { if ( field != null ) @@ -320,6 +330,8 @@ public class ValueUtils { case ref_logger : case ref_formatterLogger : value = getLogger(componentType, targetType, componentContext, refPair); break; + case ref_optional : value = Optional.ofNullable(refPair.getServiceObject(componentContext)); + break; default: value = null; } return value; diff --git a/scr/src/main/java/org/apache/felix/scr/impl/inject/field/FieldHandler.java b/scr/src/main/java/org/apache/felix/scr/impl/inject/field/FieldHandler.java index ce6f926..a5d25bd 100644 --- a/scr/src/main/java/org/apache/felix/scr/impl/inject/field/FieldHandler.java +++ b/scr/src/main/java/org/apache/felix/scr/impl/inject/field/FieldHandler.java @@ -25,6 +25,7 @@ import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArraySet; @@ -129,23 +130,32 @@ public class FieldHandler } if ( fieldType == ClassUtils.LIST_CLASS ) { - this.setFieldValue(componentInstance, new CopyOnWriteArrayList<>()); + this.setFieldValue(componentInstance, + new CopyOnWriteArrayList<>()); } else { - this.setFieldValue(componentInstance, new CopyOnWriteArraySet<>()); + this.setFieldValue(componentInstance, + new CopyOnWriteArraySet<>()); } } } } else { - // only optional field need initialization - if ( metadata.isOptional() ) - { - // null the field if optional and unary - this.setFieldValue(componentInstance, null); - } + // only optional field need initialization + if (metadata.isOptional()) + { + if (valueType == ValueType.ref_optional) + { + this.setFieldValue(componentInstance, Optional.empty()); + } + else + { + // null the field if optional and unary + this.setFieldValue(componentInstance, null); + } + } } } catch ( final InvocationTargetException ite) @@ -182,7 +192,7 @@ public class FieldHandler { // unary references - // unbind needs only be done, if reference is dynamic and optional + // unbind needs only be done, if reference is dynamic and optional if ( mType == METHOD_TYPE.UNBIND ) { if ( this.metadata.isOptional() && !this.metadata.isStatic() ) @@ -190,7 +200,15 @@ public class FieldHandler // we only reset if it was previously set with this value if ( bp.getComponentContext().getBoundValues(metadata.getName()).size() == 1 ) { - this.setFieldValue(componentInstance, null); + if (valueType == ValueType.ref_optional) + { + this.setFieldValue(componentInstance, Optional.empty()); + } + else + { + // null the field if optional and unary + this.setFieldValue(componentInstance, null); + } } } bp.getComponentContext().getBoundValues(metadata.getName()).remove(refPair); @@ -518,8 +536,11 @@ public class FieldHandler //??? this resolves which we need.... better way? if (rawParameter.getRefPair().getServiceObject(rawParameter.getComponentContext()) == null && handler.fieldExists( rawParameter.getComponentContext().getLogger() ) - && (handler.valueType == ValueType.ref_serviceType || handler.valueType == ValueType.ref_tuple - || handler.valueType == ValueType.ref_logger || handler.valueType == ValueType.ref_formatterLogger) ) + && (handler.valueType == ValueType.ref_serviceType // + || handler.valueType == ValueType.ref_tuple + || handler.valueType == ValueType.ref_logger + || handler.valueType == ValueType.ref_formatterLogger + || handler.valueType == ValueType.ref_optional)) { return rawParameter.getRefPair().getServiceObject(rawParameter.getComponentContext(), context); } diff --git a/scr/src/main/java/org/apache/felix/scr/impl/inject/internal/ClassUtils.java b/scr/src/main/java/org/apache/felix/scr/impl/inject/internal/ClassUtils.java index dcdd6b7..04b7a36 100755 --- a/scr/src/main/java/org/apache/felix/scr/impl/inject/internal/ClassUtils.java +++ b/scr/src/main/java/org/apache/felix/scr/impl/inject/internal/ClassUtils.java @@ -21,6 +21,7 @@ package org.apache.felix.scr.impl.inject.internal; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Optional; import org.apache.felix.scr.impl.logger.ComponentLogger; import org.apache.felix.scr.impl.logger.InternalLogger.Level; @@ -55,6 +56,8 @@ public class ClassUtils public static final Class<?> COLLECTION_CLASS = Collection.class; public static final Class<?> LIST_CLASS = List.class; + public static final Class<?> OPTIONAL_CLASS = Optional.class; + public static final Class<?> COMPONENT_CONTEXT_CLASS = ComponentContext.class; public static final Class<?> BUNDLE_CONTEXT_CLASS = BundleContext.class; public static final Class<?> INTEGER_CLASS = Integer.class; diff --git a/scr/src/main/java/org/apache/felix/scr/impl/inject/internal/ComponentConstructorImpl.java b/scr/src/main/java/org/apache/felix/scr/impl/inject/internal/ComponentConstructorImpl.java index 0e44eb0..5d9fc5a 100755 --- a/scr/src/main/java/org/apache/felix/scr/impl/inject/internal/ComponentConstructorImpl.java +++ b/scr/src/main/java/org/apache/felix/scr/impl/inject/internal/ComponentConstructorImpl.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import org.apache.felix.scr.impl.inject.ComponentConstructor; @@ -279,7 +280,8 @@ public class ComponentConstructorImpl<S> implements ComponentConstructor<S> && (constructorArgTypes[i] == ValueType.ref_serviceType || constructorArgTypes[i] == ValueType.ref_tuple || constructorArgTypes[i] == ValueType.ref_logger - || constructorArgTypes[i] == ValueType.ref_formatterLogger) ) + || constructorArgTypes[i] == ValueType.ref_formatterLogger + || constructorArgTypes[i] == ValueType.ref_optional)) { refPair.getServiceObject(componentContext, componentContext.getBundleContext()); } @@ -294,6 +296,12 @@ public class ComponentConstructorImpl<S> implements ComponentConstructor<S> } } } + // check for optional ref type; if null then use empty Optional + if (ref == null && constructorArgTypes[i] == ValueType.ref_optional) + { + ref = Optional.empty(); + } + if ( !refMetadata.isMultiple()) { if (ref == null && !refMetadata.isOptional()) diff --git a/scr/src/test/java/org/apache/felix/scr/integration/ComponentOptionalTest.java b/scr/src/test/java/org/apache/felix/scr/integration/ComponentOptionalTest.java new file mode 100644 index 0000000..efd13e7 --- /dev/null +++ b/scr/src/test/java/org/apache/felix/scr/integration/ComponentOptionalTest.java @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.felix.scr.integration; + + +import static org.junit.Assert.assertEquals; + +import org.apache.felix.scr.integration.components.ConstructorSingleReference; +import org.apache.felix.scr.integration.components.InjectOptionalComponent; +import org.apache.felix.scr.integration.components.InjectOptionalComponent.Mode; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.junit.PaxExam; +import org.osgi.service.component.runtime.dto.ComponentConfigurationDTO; + + +@RunWith(PaxExam.class) +public class ComponentOptionalTest extends ComponentTestBase +{ + + static + { + // use different components + descriptorFile = "/integration_test_inject_optional.xml"; + + // uncomment to enable debugging of this test class + // paxRunnerVmOption = DEBUG_VM_OPTION; + } + + static final String SINGLE_REFERENCE1 = "SingleReference1"; + static final String SINGLE_REFERENCE2 = "SingleReference2"; + + private void doTest(Mode mode) throws Exception + { + final String componentname = mode.name(); + ComponentConfigurationDTO cc = getDisabledConfigurationAndEnable(componentname, + mode.getInitialState()); + assertEquals(mode.getInitCount(), cc.description.init); + + InjectOptionalComponent cmp1 = null; + if (!mode.isMandatory()) + { + cmp1 = this.getServiceFromConfiguration(cc, InjectOptionalComponent.class); + cmp1.checkMode(mode, null); + } + + ComponentConfigurationDTO ref1DTO = getDisabledConfigurationAndEnable( + SINGLE_REFERENCE1, + mode.isDynamic() && !mode.isMandatory()// + ? ComponentConfigurationDTO.ACTIVE + : ComponentConfigurationDTO.SATISFIED); + ConstructorSingleReference ref1Service = getServiceFromConfiguration(ref1DTO, + ConstructorSingleReference.class); + + cc = findComponentConfigurationByName(componentname, mode.getSecondState()); + InjectOptionalComponent cmp2 = this.getServiceFromConfiguration(cc, + InjectOptionalComponent.class); + + if (!mode.isMandatory()) + { + assertEquals(cmp1, cmp2); + } + + if (mode.isMandatory() || mode.isDynamic()) + { + cmp2.checkMode(mode, ref1Service); + } + else + { + cmp2.checkMode(mode, null); + } + + ComponentConfigurationDTO ref2DTO = getDisabledConfigurationAndEnable( + SINGLE_REFERENCE2, ComponentConfigurationDTO.SATISFIED); + + ConstructorSingleReference ref2Service = getServiceFromConfiguration(ref2DTO, + ConstructorSingleReference.class); + + if (mode.isMandatory() || mode.isDynamic()) + { + cmp2.checkMode(mode, ref1Service); + } + else + { + cmp2.checkMode(mode, null); + } + + disableAndCheck(ref1DTO); + + if (mode.isDynamic()) + { + cmp2.checkMode(mode, ref2Service); + } + else if (mode.isMandatory()) + { + cc = findComponentConfigurationByName(componentname, + ComponentConfigurationDTO.SATISFIED); + InjectOptionalComponent cmp3 = this.getServiceFromConfiguration(cc, + InjectOptionalComponent.class); + cmp3.checkMode(mode, ref2Service); + } + else // is optional + { + cmp2.checkMode(mode, null); + } + + disableAndCheck(cc); + + cc = getDisabledConfigurationAndEnable(componentname, + ComponentConfigurationDTO.SATISFIED); + assertEquals(mode.getInitCount(), cc.description.init); + cmp1 = this.getServiceFromConfiguration(cc, InjectOptionalComponent.class); + + cmp1.checkMode(mode, ref2Service); + + disableAndCheck(cc); + } + + @Test + public void test_field_static_optional() throws Exception + { + doTest(Mode.FIELD_STATIC_OPTIONAL); + } + + @Test + public void test_field_dynamic_optional() throws Exception + { + doTest(Mode.FIELD_DYNAMIC_OPTIONAL); + } + + @Test + public void test_field_static_mandatory() throws Exception + { + doTest(Mode.FIELD_STATIC_MANDATORY); + } + + @Test + public void test_field_dynamic_mandatory() throws Exception + { + doTest(Mode.FIELD_DYNAMIC_MANDATORY); + } + + @Test + public void test_constructor_mandatory() throws Exception + { + doTest(Mode.CONSTRUCTOR_MANDATORY); + } + + @Test + public void test_constructor_optional() throws Exception + { + doTest(Mode.CONSTRUCTOR_OPTIONAL); + } +} diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/ConstructorSingleReference.java b/scr/src/test/java/org/apache/felix/scr/integration/components/ConstructorSingleReference.java index 0905f8e..96e0e37 100644 --- a/scr/src/test/java/org/apache/felix/scr/integration/components/ConstructorSingleReference.java +++ b/scr/src/test/java/org/apache/felix/scr/integration/components/ConstructorSingleReference.java @@ -18,8 +18,14 @@ */ package org.apache.felix.scr.integration.components; +import java.util.concurrent.atomic.AtomicLong; + public class ConstructorSingleReference { + private static final AtomicLong instances = new AtomicLong(); + + private final long instanceID = instances.incrementAndGet(); + public @interface Config { String name(); @@ -37,4 +43,10 @@ public class ConstructorSingleReference { return this.name; } + + @Override + public String toString() + { + return getClass().getSimpleName() + '-' + instanceID; + } } diff --git a/scr/src/test/java/org/apache/felix/scr/integration/components/InjectOptionalComponent.java b/scr/src/test/java/org/apache/felix/scr/integration/components/InjectOptionalComponent.java new file mode 100644 index 0000000..90cbd1d --- /dev/null +++ b/scr/src/test/java/org/apache/felix/scr/integration/components/InjectOptionalComponent.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.felix.scr.integration.components; + + +import java.util.Map; +import java.util.Optional; + +import org.osgi.service.component.ComponentConstants; +import org.osgi.service.component.runtime.dto.ComponentConfigurationDTO; + +public class InjectOptionalComponent +{ + public enum Mode + { + FIELD_STATIC_MANDATORY(1, false, true), // + FIELD_DYNAMIC_MANDATORY(1, true, true), // + FIELD_STATIC_OPTIONAL(1, false, false), // + FIELD_DYNAMIC_OPTIONAL(1, true, false), // + CONSTRUCTOR_MANDATORY(2, false, true), // + CONSTRUCTOR_OPTIONAL(2, false, false); + + final int initCount; + final int initialState; + final int secondState; + final boolean isMandatory; + final boolean isDynamic; + + Mode(int initCount, boolean isDynamic, boolean isMandatory) + { + this.initCount = initCount; + this.initialState = isMandatory + ? ComponentConfigurationDTO.UNSATISFIED_REFERENCE + : ComponentConfigurationDTO.SATISFIED; + this.secondState = isMandatory ? ComponentConfigurationDTO.SATISFIED + : ComponentConfigurationDTO.ACTIVE; + this.isMandatory = isMandatory; + this.isDynamic = isDynamic; + } + + public int getInitCount() + { + return initCount; + } + + public int getInitialState() + { + return initialState; + } + + public int getSecondState() + { + return secondState; + } + + public final boolean isMandatory() + { + return isMandatory; + } + + public final boolean isDynamic() + { + return isDynamic; + } + + } + + private final Mode mode; + + private final Optional<ConstructorSingleReference> refConstructor; + private Optional<ConstructorSingleReference> refFieldStatic = null; + private volatile Optional<ConstructorSingleReference> refFieldDynamic = null; + + public InjectOptionalComponent(Map<String, Object> props, Optional<ConstructorSingleReference> single) + { + this.mode = getMode(props); + this.refConstructor = single; + } + + public InjectOptionalComponent(Map<String, Object> props) + { + this(props, null); + } + + private Mode getMode(Map<String, Object> props) + { + return Mode.valueOf((String) props.get(ComponentConstants.COMPONENT_NAME)); + } + + public boolean checkMode(Mode mode, ConstructorSingleReference expected) + { + if (this.mode != mode) + { + throw new AssertionError( + "Wrong mode, expected \"" + mode + "\" but was\"" + this.mode); + } + Optional<ConstructorSingleReference> optional = getOptional(); + if (expected != null) + { + return optional.map((c) -> checkExpected(expected, c)).orElseGet( + () -> throwAssertionError( + "FAILED - expected: " + expected + " but got empty optional.")); + } + else + { + return optional.map( + (c) -> throwAssertionError( + "FAILED - expected empty optional but got: " + c)).orElse(true); + } + } + + private boolean throwAssertionError(String message) + { + throw new AssertionError(message); + } + + private boolean checkExpected(ConstructorSingleReference expected, + ConstructorSingleReference actual) + { + if (expected == actual) + { + return true; + } + throw new AssertionError( + "FAILED - expected: " + expected + " bug got: " + actual); + } + + private Optional<ConstructorSingleReference> getOptional() + { + switch (mode) + { + case CONSTRUCTOR_MANDATORY: + case CONSTRUCTOR_OPTIONAL: + return refConstructor; + case FIELD_DYNAMIC_MANDATORY: + case FIELD_DYNAMIC_OPTIONAL: + return refFieldDynamic; + case FIELD_STATIC_MANDATORY: + case FIELD_STATIC_OPTIONAL: + return refFieldStatic; + default: + throw new UnsupportedOperationException(String.valueOf(mode)); + } + } + +} diff --git a/scr/src/test/resources/integration_test_inject_optional.xml b/scr/src/test/resources/integration_test_inject_optional.xml new file mode 100644 index 0000000..a40be9d --- /dev/null +++ b/scr/src/test/resources/integration_test_inject_optional.xml @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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. +--> +<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.4.0"> + + <!-- Static Field --> + <scr:component name="FIELD_STATIC_MANDATORY" enabled="false" + init="1"> + <implementation class="org.apache.felix.scr.integration.components.InjectOptionalComponent" /> + <service factory="false"> + <provide interface="org.apache.felix.scr.integration.components.InjectOptionalComponent"/> + </service> + <reference + name="refFieldStatic" + interface="org.apache.felix.scr.integration.components.ConstructorSingleReference" + field="refFieldStatic" + cardinality="1..1" + /> + </scr:component> + + <scr:component name="FIELD_STATIC_OPTIONAL" enabled="false" + init="1"> + <implementation class="org.apache.felix.scr.integration.components.InjectOptionalComponent" /> + <service factory="false"> + <provide interface="org.apache.felix.scr.integration.components.InjectOptionalComponent"/> + </service> + <reference + name="refFieldStatic" + interface="org.apache.felix.scr.integration.components.ConstructorSingleReference" + field="refFieldStatic" + cardinality="0..1" + /> + </scr:component> + + <!-- Dynamic Field --> + <scr:component name="FIELD_DYNAMIC_MANDATORY" enabled="false" + init="1"> + <implementation class="org.apache.felix.scr.integration.components.InjectOptionalComponent" /> + <service factory="false"> + <provide interface="org.apache.felix.scr.integration.components.InjectOptionalComponent"/> + </service> + <reference + name="refFieldDynamic" + interface="org.apache.felix.scr.integration.components.ConstructorSingleReference" + field="refFieldDynamic" + cardinality="1..1" + policy="dynamic" + /> + </scr:component> + + <scr:component name="FIELD_DYNAMIC_OPTIONAL" enabled="false" + init="1"> + <implementation class="org.apache.felix.scr.integration.components.InjectOptionalComponent" /> + <service factory="false"> + <provide interface="org.apache.felix.scr.integration.components.InjectOptionalComponent"/> + </service> + <reference + name="refFieldDynamic" + interface="org.apache.felix.scr.integration.components.ConstructorSingleReference" + field="refFieldDynamic" + cardinality="0..1" + policy="dynamic" + /> + </scr:component> + + <!-- mandatory constructor --> + <scr:component name="CONSTRUCTOR_MANDATORY" enabled="false" + init="2"> + <implementation class="org.apache.felix.scr.integration.components.InjectOptionalComponent" /> + <service factory="false"> + <provide interface="org.apache.felix.scr.integration.components.InjectOptionalComponent"/> + </service> + <reference interface="org.apache.felix.scr.integration.components.ConstructorSingleReference" + cardinality="1..1" + parameter="1"/> + </scr:component> + + <!-- optional constructor --> + <scr:component name="CONSTRUCTOR_OPTIONAL" enabled="false" + init="2"> + <implementation class="org.apache.felix.scr.integration.components.InjectOptionalComponent" /> + <service factory="false"> + <provide interface="org.apache.felix.scr.integration.components.InjectOptionalComponent"/> + </service> + <reference interface="org.apache.felix.scr.integration.components.ConstructorSingleReference" + cardinality="0..1" + parameter="1"/> + </scr:component> + + <scr:component name="SingleReference1" enabled="false" activate="activator"> + <implementation class="org.apache.felix.scr.integration.components.ConstructorSingleReference" /> + <service factory="false"> + <provide interface="org.apache.felix.scr.integration.components.ConstructorSingleReference"/> + </service> + <property name="name" value="single"/> + </scr:component> + + <scr:component name="SingleReference2" enabled="false" activate="activator"> + <implementation class="org.apache.felix.scr.integration.components.ConstructorSingleReference" /> + <service factory="false"> + <provide interface="org.apache.felix.scr.integration.components.ConstructorSingleReference"/> + </service> + <property name="name" value="single"/> + </scr:component> +</components>
