Sunday, July 30, 2017

Migration of SCR annotations to OSGi R6 annotations in AEM 6.3


I have recently gone through a migration project from an older version of AEM to AEM 6.3 and the major challenge while migrating java classes is that sometimes the API become deprecated in the newer version.

From AEM 6.2, One of our favorite Apache Felix Maven SCR Plugin has been deprecated and in place of this, Maven Bundle Plugin (version 6.2 or greater) has been introduced.
It always take a small effort to adapt new things, But change is the nature’s law. So here we will discuss how OSGi plugin has replaced all SCR annotations in AEM.
Packages:
Now the package  “org.apache.felix.scr.annotations.*” will be replaced with “org.osgi.service.component.annotations.*”  and “org,osgi.service.metatype.annotations.*
While using Maven SCR plugin, we can find the Declarative Service(DS) output under /target/classes/OSGi-INF but using OSGi Bundle Plugin our DS output are found in /OSGI-INF, packaged under the compiled JAR File.

Note:The Apache Felix SCR annotations are replaced in AEM 6.3 projects with OSGi R6 annotations

How To start with OSGi Annotations?

1.Add a plugin in Parent Pom.xml
<plugin>
 <groupId>org.apache.felix</groupId>
 <artifactId>maven-bundle-plugin</artifactId>
 <version>3.2.0</version>
 <inherited>true</inherited>
</plugin>

2.Add the following maven dependencies.
<dependency>
 <groupId>org.osgi</groupId>
 <artifactId>org.osgi.service.component.annotations</artifactId>
 <version>1.3.0</version>
</dependency>

<dependency>
 <groupId>org.osgi</groupId>
 <artifactId>org.osgi.annotation</artifactId>
 <version>6.0.0</version>
</dependency>

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

<dependency>
 <groupId>org.osgi</groupId>
 <artifactId>org.osgi.compendium</artifactId>
 <version>4.2.0</version>

<scope>provided</scope>
</dependency>

NoteMake sure that while building the code,plugin is working fine.

Migration of OSGi Components and Service

Before : In SCR Annotations:

@Component(name=”Test Service”,immediate=true,description="this is description")
@Service(TestService.class)
public class TestService {
                     public String getName()
{
return "testService";
}
}

After:In OSGi Annotations
@Component(name=”Test Service” service=TestService.class,immediate=true)
public class TestService {
                     public String getName()
{
return "testService";
}
}

Migration of Servlet

OSGi annotations has reduced our effort to remember a lot of Annotations.

In the Sling Servlets:
We used to use @Component, @Service @SlingServlet @Properties in SCR Annotations.
OSGi annotation just have @Component with the collaboration of all these annotations.

Before : In SCR Annotations:
@SlingServlet(paths="/bin/servlet",selectors = {"test","test1"},extensions = "html")
public class Test extends SlingSafeMethodsServlet{
  @Override
  protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)throws IOException
  {
      response.getWriter().print(" I am in doGet Method");
  }
}

After:In OSGi Annotations

@Component(service=Servlet.class,
          property={
                  Constants.SERVICE_DESCRIPTION + "=Simple Demo Servlet",
                  "sling.servlet.methods=" + HttpConstants.METHOD_GET,
                  "sling.servlet.resourceTypes="+ "com.poc.osgiannotation/components/structure/page",
                 "sling.servlet.paths="+ "/bin/servlet",
                  "sling.servlet.extensions=" + "txt"
          })
public class SimpleServlet extends SlingSafeMethodsServlet {
{
@Override
  protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)throws IOException
  {
      response.getWriter().print(" I am in doGet Method");
  }
}

Custom OSGi Configuration in AEM

OSGi annotations provided a flexibility to create configuration in a separate interface and we can use it in any place.
The OSGi Configurations generates metatype specification that provides a way to describe configuration of the component.Such a metatype information can be used at runtime to generate forms to edit/create the configuration of a component. For example the Apache Felix web console does exactly that.

Let’s understand how to make OSGi configurations in AEM using OSGi Bundle Plugin.


configuration.PNG
Fig- OSGi COnfiguration in felix Console

Migration of Custom Workflow Process Step

Before:In SCR Annotations
@Component(immediate = true, enabled = true, metatype = true)
@Service(value = WorkflowProcess.class)
@Property(name = "process.label", value = "Activate Page", propertyPrivate = true)
public class RejectionMailProcess implements WorkflowProcess {
public void execute(WorkItem workitem, WorkflowSession wfsession,MetaDataMap metaDataMap)
{
System,.out.println(“ I am in execute method”);
}
}

After: In OSGi Annotations
@Component(service = WorkflowProcess.class, property = {"process.label=Activate Page" })
public class TestWorkflow implements WorkflowProcess {
public void execute(WorkItem workitem, WorkflowSession wfsession,MetaDataMap metaDataMap)
{
System,.out.println(“ I am in execute method”);
}
}


How to use @Reference Annotations in OSGi Annotations?

There are multiple ways to use @Reference annotations in OSGi annotations:

1.
@Reference
TestService testService;

2.
private TestService testService;
@Reference
public void bindTestService(TestService testService) {
     this.testService = testService;
}

public void unbindTestService(TestService testService) {
     this.testService = testService;
}


If you have any query or suggestion then kindly comment or mail us at sgaem.blog02@gmail.com


Hope it will help you guys !!
Thanks and Happy Learning

44 comments:

  1. Thanks for sharing. More informative comparison between earlier version with new was awesome and straightforward.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Hi,

    I am glad about this blog spot. These articles are really helpful.

    Could you please create an article on using Template Editor as well. I need to learn Template editor and there is no such helpx article. It would be of great help.

    Thanks in advance!

    Best Regards,
    Abhishek

    ReplyDelete
    Replies
    1. Hi Abhishek,
      Yes for sure. We will surely publish blogs on template editor this month.Thanks for the feedback.

      Delete
  4. Thanks for the helpful guide.

    Unfortunately I have a problem with using the 1st option of @Reference. The IDE (Eclipse) displays the error message "The annotation @Reference is disallowed for this location" if I don't bind it to a method.

    Also I would appreciate it, if you could add a description about how to migrate a replication preprocessor implementation. :)

    ReplyDelete
    Replies
    1. Hi Andras,
      I was facing the same issues in some of my project, at that time I used the second way to use @Reference annotation,but unfortunately, not able to get the reason.

      Delete
  5. Hi Shivani,

    How to declare filter class with OSGI annotation? Currently we do using SCR annotation as below.

    @SlingFilter(order = 100, scope = SlingFilterScope.REQUEST)


    Suneel

    ReplyDelete
    Replies
    1. @Component(service = Filter.class,
      property = {
      Constants.SERVICE_DESCRIPTION + "=Demo to filter incoming requests",
      EngineConstants.SLING_FILTER_SCOPE + "=" + EngineConstants.FILTER_SCOPE_REQUEST,
      Constants.SERVICE_RANKING + "=-700"

      })

      Delete
  6. Thanks Shivani.

    I have created configuration service using OSGI R6 annotations as you mentioned above. Do you know how to inject/reference the configuration service in SlingServlet?

    Suneel

    ReplyDelete
    Replies
    1. Hi Suneel,
      If you have write OSGi Service like above, you can write it like any other service.
      @Reference
      UserAccountDetail useraccountDetail;
      and call the method.

      Delete
    2. Hi Shivani, I tried this approach, for some reason it gives me null object. I am using AEM 6.2 version. If I create regular OSGI service (other than config) then @Reference is working. Please let me know what could be wrong here.

      Suneel

      Delete
    3. Updated the maven dependency for the above issue.

      Delete
    4. Hi Shivani/Suneel,

      I too have the same issue with @Reference annotation. I am currently using AEM 6.3 version. Both approach 1 and 2 are not working for me. The object referred shows up as null. Do you have any other approach?

      Delete
    5. Have you checked your maven dependencies????? Put this dependency above all dependencies.

      Delete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Thank you for your article. But I have one point to your article. I tried to create Workflow Step with new annotation and I found, that for registration new workflow step I need specify service.pid in manual, but old felix plugin specify it automatically. So example of working workflow is:


    @Component(service = WorkflowProcess.class,
    property = {
    "process.label=MyTrainingWorkflowProcess",
    "service.pid=com.aem.samples.core.workflow.MyTrainingWorkflowProcess"
    })
    public class MyTrainingWorkflowProcess implements WorkflowProcess {
    @Override
    public void execute(WorkItem workItem, WorkflowSession workflowSession, MetaDataMap metaDataMap) throws WorkflowException {

    }
    }


    So, please, fix your example of workflow with new annotation.

    ReplyDelete
    Replies
    1. Work flow MessageGatewayService(for email notification) using Gateway method throwing null pointer using R6 osgi annotation? Do we have any references for this?

      Delete
  9. I created an osgi config as described above and using it in service class but the service pid doesnot show up in bundle. it seems if I make configuration POlicy as optional or if i save my configuration explicitly via configMgr, service PID starts showing up.Do we have to bind or initialize config objects.

    ReplyDelete
  10. Hi,

    Thanks for your blog. Could you please help with an article on the migration of OSGI run mode configurations from SCR to OSGI R6 anotations.

    ReplyDelete
  11. Excellent. Appreciate you effort!

    ReplyDelete
  12. @Component
    @Service(value = org.apache.sling.api.adapter.AdapterFactory.class)
    @Properties({
    @Property(name="service.ranking", intValue = 9999),
    @Property(name="adaptables", value={"org.apache.sling.api.resource.Resource"}),
    @Property(name="adapters",
    value= {"com.cq.services.pagetypes.util.AgcoObject",
    "com.cq.services.pagetypes.util.AgcoPage"
    })
    })

    hi, how to migrate multi value property. here adapters is multi value. please help

    ReplyDelete
    Replies
    1. Hi Abhinash,

      Did you get any solution ?

      Regards,
      Swati

      Delete
    2. Abhinash Kumar SinghOctober 11, 2018 at 12:23 PM

      Yes I found the solution
      @Component(service = "org.apache.sling.api.adapter.AdapterFactory.class", property = {
      "service.ranking=9999",
      "adaptables=org.apache.sling.api.resource.Resource",
      "adapters=com.cq.services.pagetypes.util.AgcoObject","adapters=com.cq.services.pagetypes.util.AgcoPage"})

      Delete
    3. This comment has been removed by the author.

      Delete
    4. hi,

      then how will we change the annotations for below code :

      @Service
      @Component(metatype = true, immediate = true, label = "Content Insights Page Name Provider", description = "Service to use custom content insights page name variable")
      @Properties({
      @Property(name = Constants.SERVICE_DESCRIPTION, value = "Content Insights Page Name Resolver implementation"),
      @Property(name = Constants.SERVICE_RANKING, intValue = 200, propertyPrivate = false),
      @Property(name = "basePath", value = "/content/myproject/language-masters/en", description="The path to be removed from Content Insights URL", label="basePath"),
      @Property(name = "blogBasePath", value = "/content/myproject-blog/en", description="The path to be removed from Content Insights URL for Blogs", label="blogBasePath"),
      @Property(name = "cqVarContentInsight", value = "pagedata.path", description="The CQ variable used for Content Insights URL", label="cqVarContentInsight"),
      @Property(name = "langBasePath", value = "/content/myproject/language-masters", description="The path to be removed from Content Insights URL", label="langBasePath")})

      Delete
  13. Nice article.

    One thing which puzzles me that with R6 approach, you need to remember a lot more information than SCR annotation.

    Secondly as properties are being used to pass key/val pair, you have to remember key value pair for properties. Also editors will not be of much help which was unlike with SCR based annotations.

    ReplyDelete
  14. hi can yo show how the cronexpression ie scheduler.expression property can be defined in the OCD . The scheduler class based on this OCD doesn't get executed (ie the run()method) .

    ReplyDelete
  15. This comment has been removed by the author.

    ReplyDelete
  16. Hi,

    Can anyone help me to rewrite the below annotations in AEM 6.4 supported way?

    @Component(configurationFactory=true, immediate=true, metatype = true, createPid = false, policy = ConfigurationPolicy.REQUIRE,
    name = "IntegrationsServiceConfiguration", label="TestProj: Integration Services Configuration", description="Configuration Factory for all Integration Services")
    @Properties({
    @Property(name = "service.type", label="Type of Service", description="Select a Service Type",
    options = {
    @PropertyOption(name = "REST Service", value = "REST Service")
    }
    ),
    @Property(name="service.identifier", label="Service Identifier", value="", description="Select a Service Identifier"),
    @Property(name="service.url", label="URL", value="", description="Enter the service URL"),
    @Property(name="service.authentication", label="Authentication", boolValue =false, description="Does the service requires authentication?"),
    @Property(name="service.username", label="Service Username", value="", description="Enter the Username required for authentication"),
    @Property(name="service.password", label="Service Password", value="", description="Enter the Password required for authentication"),
    @Property(name="service.secured.url", label="Secured URL", boolValue =false, description="Should service connect over the secured https connection"),
    @Property(name="service.http.method", label="Http Method", description="Select the Http method to call the Service",
    options = {
    @PropertyOption(name = "", value = "-- Select One --"),
    @PropertyOption(name = "GET", value = "GET"),
    @PropertyOption(name = "POST", value = "POST"),
    @PropertyOption(name = "PUT", value = "PUT"),
    @PropertyOption(name = "DELETE", value = "DELETE")
    }
    ),
    @Property(name="service.params", label="Additional Configuration Params", cardinality = 25,value ="" ,description="Add one key:value pair per line with \":\" as delimiter"),
    @Property(name="service.xapikey", label="x-api-key", value="", description="Enter the x-api-key"),
    })

    Thanks,
    Abhinav.

    ReplyDelete
    Replies
    1. Hi Abhi,

      Did you get the solution for this? If so what's that?

      Delete
    2. Hi Vijay,

      I didn't get the solution yet.

      Thanks,
      Abhinav.

      Delete
    3. Hi abhi,

      Did you get any solution because I am also facing the same issue ?
      Regards,
      Swati

      Delete
  17. This comment has been removed by the author.

    ReplyDelete
  18. I am getting this error "annotation type not applicable to this kind of declaration
    "
    when I try @Reference
    TestService testService; instead of usea method, any help?, please

    ReplyDelete
  19. How to convert OSGi service factory from scr annotations

    ReplyDelete