This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-agent-skills.git
commit ad16a854026616e209ebab9cf5dd93161d5c4d51 Author: Robert Munteanu <[email protected]> AuthorDate: Fri Apr 17 17:28:01 2026 +0200 skills: more osgi annotation migration instructions --- .../reference/osgi-annotations.md | 269 ++++++++++++++++++--- 1 file changed, 242 insertions(+), 27 deletions(-) diff --git a/update-sling-parent-pom/reference/osgi-annotations.md b/update-sling-parent-pom/reference/osgi-annotations.md index ffbae0d..56978da 100644 --- a/update-sling-parent-pom/reference/osgi-annotations.md +++ b/update-sling-parent-pom/reference/osgi-annotations.md @@ -1,24 +1,24 @@ # OSGi Annotations -Replace usage of the deprecated Apache Felix SCR annotations with the official OSGi annotations. +Replace deprecated Apache Felix SCR annotations with the OSGi Declarative Services and OSGi Metatype annotations. ## pom.xml -Remove the following plugins: -- org.apache.felix:maven-scr-plugin +Remove: -Remove the following dependencies: -- org.apache.felix:org.apache.felix.scr.annotations +- `org.apache.felix:maven-scr-plugin` +- `org.apache.felix:org.apache.felix.scr.annotations` +## Imports -## Java imports - -Imports to remove +Remove Felix SCR imports such as: ```java import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Modified; +import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.PropertyUnbounded; import org.apache.felix.scr.annotations.Reference; @@ -27,12 +27,13 @@ import org.apache.felix.scr.annotations.ReferencePolicy; import org.apache.felix.scr.annotations.Service; ``` -Imports to add +Add OSGi imports as needed: ```java import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Modified; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; @@ -41,57 +42,195 @@ import org.osgi.service.metatype.annotations.Designate; import org.osgi.service.metatype.annotations.ObjectClassDefinition; ``` -## The @Service annotation +## Annotation mapping + +Use this mapping: + +- `@Component` -> `@Component` +- `@Service` -> `@Component(service = ...)` +- `@Reference` -> `@Reference` +- `@Activate` -> `@Activate` +- `@Deactivate` -> `@Deactivate` +- `@Modified` -> `@Modified` +- `@Property` and `@Properties` -> typed configuration annotation plus OSGi metatype annotations; use `@Component(property = ...)` only for simple fixed service properties + +## `@Component` -The `@Service` annotation is no longer needed. Instead, the `service` attribute of the `@Component` annotation should be used to specify the service interfaces. +Replace the Felix SCR annotation with `org.osgi.service.component.annotations.Component`. + +```java +@Component +public class MyComponent { +} +``` + +## `@Service` + +Remove `@Service`. + +List provided service interfaces on `@Component(service = ...)`. Before: -``java +```java @Component @Service(MyService.class) +public class MyComponent implements MyService { +} ``` -After +After: ```java @Component(service = MyService.class) +public class MyComponent implements MyService { +} ``` -## The @Property annotation for a top-level class - -The `@Property` annotation is replaced by the 'property' attribute of the `@Component` annotation. +If the component must not register a service, set `service = {}` explicitly. Before: ```java @Component -@Property(name = Constants.SERVICE_DESCRIPTION, value = "Apache Sling MIME Type Service") +public class MyComponent { +} ``` After: ```java -@Component - property = { - Constants.SERVICE_DESCRIPTION + "=Apache Sling MIME Type Service" - }) +@Component(service = {}) +public class MyComponent { +} ``` -## The @Properties annotation +Always make the intended service registration explicit. + +## `@Reference` -The `@Properties` annotation is replaced by the 'property' attribute of the `@Component` annotation. +Field references can usually be migrated directly. Before: + +```java +@Reference +private EventAdmin eventAdmin; +``` + +After: + +```java +@Reference +private EventAdmin eventAdmin; +``` + +For method-based references, place `@Reference` on the bind method. + +Before: + +```java +@Reference(bind = "bindEventAdmin", unbind = "unbindEventAdmin") +private EventAdmin eventAdmin; + +protected void bindEventAdmin(final EventAdmin service) { + this.eventAdmin = service; +} + +protected void unbindEventAdmin(final EventAdmin service) { + if (this.eventAdmin == service) { + this.eventAdmin = null; + } +} +``` + +After: + +```java +private EventAdmin eventAdmin; + +@Reference(unbind = "unbindEventAdmin") +protected void bindEventAdmin(final EventAdmin service) { + this.eventAdmin = service; +} + +protected void unbindEventAdmin(final EventAdmin service) { + if (this.eventAdmin == service) { + this.eventAdmin = null; + } +} +``` + +Retain existing reference options when present, for example cardinality and policy. + +## Lifecycle annotations + +Migrate lifecycle methods one-to-one. + +Before: + +```java +@Activate +protected void activate() { +} + +@Modified +protected void modified() { +} + +@Deactivate +protected void deactivate() { +} +``` + +After: + +```java +@Activate +protected void activate() { +} + +@Modified +protected void modified() { +} + +@Deactivate +protected void deactivate() { +} +``` + +When migrating configuration-heavy components, prefer typed configuration parameters on lifecycle methods. + +```java +@Activate +protected void activate(final MyComponentConfig config) { +} + +@Modified +protected void modified(final MyComponentConfig config) { +} +``` + +## Replacing `@Property` and `@Properties` + +Use one of these approaches. + +Use `@Component(property = ...)` for simple fixed service properties. + +Before: + ```java @Component @Properties({ @Property(name = "sling.servlet.extensions", value = "json"), @Property(name = "sling.servlet.resourceTypes", value = "sling/bar") }) +public class MyServlet { +} ``` After: + ```java @Component( property = { @@ -99,11 +238,87 @@ After: "sling.servlet.resourceTypes=sling/bar" } ) +public class MyServlet { +} +``` + +Use a typed configuration annotation for component configuration formerly described with `@Property`. + +```java +@ObjectClassDefinition( + name = "My Component Configuration", + description = "Configuration for MyComponent" +) +public @interface MyComponentConfig { + + @AttributeDefinition(name = "Welcome Message") + String welcome_message() default "Hello World!"; + + @AttributeDefinition(name = "Welcome Count") + int welcome_count() default 3; + + @AttributeDefinition(name = "Output Goodbye") + boolean output_goodbye() default true; +} ``` -## The ReferenceCardinality enum +Bind that configuration to the component with `@Designate` and consume it in `@Activate` and `@Modified`. + +```java +@Component(service = {}) +@Designate(ocd = MyComponentConfig.class) +public class MyComponent { + + @Activate + protected void activate(final MyComponentConfig config) { + } + + @Modified + protected void modified(final MyComponentConfig config) { + } +} +``` + +Property name mapping for typed configuration follows OSGi rules. In particular, `_` in an annotation method name maps to `.` in the configuration property name. + +Example: + +```java +String service_ranking() default "100"; +``` + +maps to the configuration property `service.ranking`. + +## Metatype annotations + +Use these OSGi metatype annotations when the old Felix SCR `@Property` metadata was used to describe editable configuration: + +- `@ObjectClassDefinition` on the configuration annotation +- `@AttributeDefinition` on configuration properties +- `@Designate(ocd = ...)` on the component class + +## Enum value changes + +Update enum values where needed. + +`ReferenceCardinality`: + +- `OPTIONAL_UNARY` -> `OPTIONAL` +- `OPTIONAL_MULTIPLE` -> `MULTIPLE` +- `MANDATORY_UNARY` -> `MANDATORY` +- `MANDATORY_MULTIPLE` -> `AT_LEAST_ONE` + +Keep `ReferencePolicy.STATIC` and `ReferencePolicy.DYNAMIC` unchanged when already specified. + +## Migration checklist -The enum values have changed: +For each migrated component: -- OPTIONAL_UNARY → OPTIONAL -- OPTIONAL_MULTIPLE → MULTIPLE +1. Remove Felix SCR imports. +2. Replace `@Service` with `@Component(service = ...)`. +3. Add `service = {}` for components that should not register a service. +4. Keep field `@Reference` injections or move method-based references to annotations on bind methods. +5. Replace Felix lifecycle annotations with the OSGi lifecycle annotations. +6. Replace simple fixed `@Property` and `@Properties` usage with `@Component(property = ...)`. +7. Replace configurable `@Property` usage with a typed configuration annotation using `@ObjectClassDefinition`, `@AttributeDefinition`, and `@Designate`. +8. Update changed `ReferenceCardinality` enum values.
