May 26, 2020
Estimated Post Reading Time ~

How to Migrate from SCR Annotations to DS Annotations

Change is a rule of life, and if you are not updating yourself on current trends then you are constantly falling behind on new updates that can be essential to your system. With Adobe, they release a new version of AEM  every year. Last year, they introduced SCR annotations, and now they are supporting DS annotations. From AEM 6.2 DS Annotations are supported, and it is highly recommended that you use these in the newer version of AEM. This is why you need to learn how to migrate from SCR Annotations to DS Annotations.

Let’s look at brief introductions of SCR Annotations and DS Annotations.

What are SCR Annotations?
SCR stands for Service Component Runtime. The ”maven-scr-plugin” uses scr annotations from the corresponding subproject at Apache Felix. All annotations are in “org.apache.felix.scr.annotations” package. The Apache Felix SCR described by the OSGi Declarative Services Specification is implemented by the “org.apache.felix.scr” bundle.

SCR annotations do not support new features from R6 and above. It is highly recommended to use OSGi annotations for Declarative Services instead.

What are DS Annotations?
DS annotation is an official implementation from OSGi R6 (release 6) specification. It is also known as OSGi Declarative Services Annotations.
Remember that declarative services are a compiled time process. For DS annotations to be effective, they must be handled during the build process.
The migration from SCR annotations to DS annotations is fairly easy and both annotation styles will work side-by-side while you complete the switch-over.
So here will we discuss how to migrate from SCR annotations to DS annotations?

Plugin:
For DS annotations we have to use “maven-bundle-plugin” instead of “maven-scr-plugin”. Version 3.2.0 or greater.

<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>3.5.1</version>
<inherited>true</inherited>
</plugin>

Dependencies:
For DS annotations we need artifacts  “org.osgi.service.metatype.annotations” and “org.osgi.service.component.annotations” instead “org.osgi.core” and “org.osgi.compendium”.

<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.component.annotations</artifactId>
<version>1.3.0</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.metatype.annotations</artifactId>
<version>1.3.0</version>
</dependency>

Java Package:
In DS Annotation, package “org.apache.felix.scr.annotations.*” will be replaced with “org.osgi.service.component.annotations.*”  and “org,osgi.service.metatype.annotations.*”.

Migration of Component and Services:
We used to use @Component, @Service annotations in SCR Annotations while in DS annotation just have @Component annotation with the collaboration of all these annotations.

SCR annotation Implementation:
@Component(label = "Demo Service", immediate = true)
@Service(DemoService.class)

public class DemoService {
    public String getMyClassName() {
              return "DemoService";
    }
}

DS annotation Implementation:
@Component(name = "Demo Service", immediate = true, service = DemoService.class)

public class DemoService {
    public String getMyClassName() {
       return "DemoService";
    }
}

Migration of Sling Servlet:
We used to use @Component, @Service @SlingServlet @Properties in SCR Annotations while DS annotation just has @Component with the collaboration of all these annotations.

SCR annotation Implementation
@SlingServlet(  resourceTypes = "sling/servlet/default",
                                methods = {"POST", "GET"},
                                selectors = "demoServlet",
                                extensions = "fetch")

public class Demo extends SlingSafeMethodsServlet {
    @Override
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {  
        response.getWriter().print("doGet Method");  }

    @Override
    protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {  
        response.getWriter().print("doPost Method");
    }
}

DS annotation Implementation:
@Component(service = Servlet.class,
                               property = {
                "sling.servlet.methods=" + HttpConstants.METHOD_GET,
                "sling.servlet.methods=" + HttpConstants.METHOD_POST,
                "sling.servlet.resourceTypes=" + "sling/servlet/default",
                "sling.servlet.selectors=" + "demoServlet",
                "sling.servlet.extensions=" + "fetch"})

public class Demo extends SlingSafeMethodsServlet {
    @Override
    protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
        response.getWriter().print("doGet Method");
    }

    @Override
    protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
        response.getWriter().print("doPost Method");
    }
}

Migration of Custom Workflow process:
SCR annotations Implementation:
@Component(immediate = true, enabled = true, metatype = true)
@Service(value = WorkflowProcess.class)
@Property(name = "process.label", value = "Demo Process", propertyPrivate = true)

public class DemoProcess implements WorkflowProcess {
    public void execute(WorkItem workitem, WorkflowSession wfsession, MetaDataMap metaDataMap)  {
        System.out.println("I am in execute method");
    }
}

DS annotation Implementation:
@Component(service = WorkflowProcess.class,  
                                immediate = true, enabled = true,
                                 property = {"process.label= Demo Process"})

public class DemoProcess implements WorkflowProcess {
    public void execute(WorkItem workitem, WorkflowSession wfsession, MetaDataMap metaDataMap) {
        System.out.println(“ I am in execute method”);
    }
}

Migration of Custom OSGi Configuration:
Major changes came in custom OSGi configuration. OSGi annotations provided flexibility to create a configuration in a separate interface and we can use it in any place.

SCR annotations Implementation:
@Component(immediate = true, metatype = true, label = "Customer Configuration")
@Service(value = CustomerDemo.class)

public class CustomerDemo {
    @Property(label = "Custmer Name", value = "default value")
    private static final String CUST_NAME = "custmer.name";
   
    @Property(label = "Custmer address", value = "default value")
    private static final String CUST_ADDR = "custmer.address";
    private String custmerName;    
    private String custmerAddr;

    @Activate
    protected void activate(Map context) {
   custmerName = PropertiesUtil.toString(context.get(CUST_NAME), EMPTY);
   custmerAddr = PropertiesUtil.toString(context.get(CUST_ADDR), EMPTY);
 }  

    @Modified
    protected void modified(ComponentContext context) {
   custmerName = PropertiesUtil.toString(context.getProperties().get(CUST_NAME), EMPTY);
   custmerAddr = PropertiesUtil.toString(context.getProperties().get(CUST_ADDR), EMPTY);

 }

DS annotation Implementation:
Interface:
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
@ObjectClassDefinition(name = "Customer Configuration")

public @interface CustomerConfig {
    @AttributeDefinition(name = "Custmer Name")
    String CUST_NAME() default "default value";
    @AttributeDefinition(name = "Custmer address")
    String CUST_ADDR() default "default value";
}

Actual Class:
import org.osgi.service.component.annotations.Activate;
        import org.osgi.service.component.annotations.Component;
        import org.osgi.service.component.annotations.Modified;
        import org.osgi.service.metatype.annotations.Designate;

@Component(service = CustomerDemo.class, immediate = true)
@Designate(ocd = CustomerConfig.class)

public class CustomerDemo {  
    private String custmerName;

    private String custmerAddress;
    @Activate
    @Modified
    protected void Activate(final CustomerConfig custmerconfig) {
   custmerName = custmerconfig.CUST_NAME();
   custmerAddress = custmerconfig.CUST_ADDR();
 }
}

Fig- OSGi Configuration in Felix Console

Migration of OSGi Config:
The migration of OSGi config is quite tricky. There are two scenarios for OSGi config.

You have created a new configuration according to DS annotations and you want a config of it for a default value.
You are migrating old custom config to a new one (DS annotations) and you already have a config file.
Scenario 1:
If you have created your own custom config. For e.g.
Interface:
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
@ObjectClassDefinition(name = "Demo Configuration")

public @interface Demo {
    @AttributeDefinition(name = "My Name")
    String myName() default "Abc";
    @AttributeDefinition(name = "Address")
    String myAddress() default "default address";
}
So, your default configuration will be:
<? xml version = "1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"xmlns:jcr="http://www.jcp.org/jcr/1.0"
        jcr:primaryType="sling:OsgiConfig"
        myName="XYZ"
        myAddress="default address"/>

Scenario 2:
If you have already default configuration and you are migrating to DS annotation. For e.g.
Config file:
<?xml version="1.0" encoding="UTF-8"?> 
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" 
    jcr:primaryType="sling:OsgiConfig" 
    smtp.host="smtp.office365.com" 
    smtp.passsword="{474c6b96ab4d}" 
    smtp.port="587" 
    smtp.user="noreply.onmicrosoft.com"/> 


So, your interface will be:
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.osgi.service.metatype.annotations.AttributeDefinition;

@ObjectClassDefinition(name = "Email Configuration")
public @interface EmailConfig {
    @AttributeDefinition(name = "SMTP server host name")
    String smtp_host();

    @AttributeDefinition(name = "SMTP server port")
    String smtp_port();

    @AttributeDefinition(name = "SMTP user")
    String smtp_user();

    @AttributeDefinition(name = "SMTP password")
    String smtp_passsword();
}


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.