Lessons
  Menu

Calling External API from OSGI Service

Introduction: This article demonstrates how to create an OSGi service in Adobe Experience Manager (AEM) to fetch data from an external API and return the response. We’ll use the example of fetching recipes from a mock JSON API (https://dummyjson.com/recipes). We’ll also create a servlet to access this service and test the functionality using Postman.

Step 1: Creating the OSGi Service

Service Interface: First, define a service interface in AEM. This interface declares a method to make the API call.

package com.adobe.aem.guides.wknd.core.services;

public interface RecipeService {
    String getRecipes();
}

Package Declaration

  • package com.adobe.aem.guides.wknd.core.services;
    • This line declares the package name for the interface. Packages in Java are used to group related classes and interfaces together, helping in organizing the code and avoiding naming conflicts. In this case, the package is com.adobe.aem.guides.wknd.core.services, which suggests that this interface is part of the Adobe AEM WKND tutorial series, specifically under the core.services section.

Interface Declaration

  • public interface RecipeService { ... }
    • This line declares a public interface named RecipeService.
    • In Java, an interface is a reference type, similar to a class, that can contain only constants, method signatures, default methods, static methods, and nested types. Method bodies exist only for default methods and static methods.
    • Interfaces are abstract by nature; they cannot be instantiated and can only be implemented by classes or extended by other interfaces.
    • The public keyword means that the interface can be accessed by any other class.

Method Signature in the Interface

  • String getRecipes();
    • This is a method signature within the RecipeService interface.
    • The method getRecipes is declared without an implementation (no method body), which is typical for interface methods.
    • The method returns a String. The exact nature of this string is not specified here, but the name getRecipes suggests it might return a representation (possibly a JSON, XML, or a simple String) of recipes.
    • Since there is no implementation, any class that implements the RecipeService interface will need to provide its own implementation of the getRecipes method.

Usage in AEM Context

  • In the context of AEM, this RecipeService interface would be implemented by a class that provides the logic for retrieving recipes. This might involve fetching data from a repository, a database, or an external service.
  • The interface and its implementation enable a decoupling of the service definition from its implementation, which is a common practice in OSGi and AEM development. This allows for easier testing and maintenance of the code.

Step2: Service Implementation

Implement the service interface. Here, we use HttpClient to make the API call.

package com.adobe.aem.guides.wknd.core.services.impl;


import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.osgi.service.component.annotations.Component;
import java.io.IOException;
import com.adobe.aem.guides.wknd.core.services.RecipeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



@Component(service = RecipeService.class)
public class RecipeServiceImpl implements RecipeService {

    private static final Logger LOGGER = LoggerFactory.getLogger(RecipeService.class);

    @Override
    public String getRecipes() {
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {

            HttpGet request = new HttpGet("https://dummyjson.com/recipes");
            String response = EntityUtils.toString(httpClient.execute(request).getEntity());
            LOGGER.debug(response);
            return response;

        } catch (IOException e) {
            LOGGER.error("IO Error",e);
            return null;
        }
    }
}

Imports

  • The import statements bring in external classes and interfaces used in this class. These include classes for HTTP communication (org.apache.http), OSGi annotations (org.osgi.service.component.annotations), the RecipeService interface, and SLF4J logging (org.slf4j).

Class Declaration

  • public class RecipeServiceImpl implements RecipeService { ... }
    • This is the class definition. RecipeServiceImpl is a public class that implements the RecipeService interface.

Logger

  • private static final Logger LOGGER = LoggerFactory.getLogger(RecipeService.class);
    • This line initializes a logger specifically for this class. The logger is used to log debug and error messages.

Component Annotation

  • @Component(service = RecipeService.class)
    • This is an OSGi annotation that registers this class as an OSGi service component. The service it provides is RecipeService. This means in the context of AEM/OSGi, this implementation (RecipeServiceImpl) will be used whenever a RecipeService is required.

Method Implementation

  • public String getRecipes() { ... }
    • This method is an implementation of the getRecipes method declared in the RecipeService interface. It overrides the interface method to provide the actual functionality.

Method Logic

  • The method getRecipes uses Apache HTTPClient (CloseableHttpClient) to make a GET request to a specified URL (https://dummyjson.com/recipes).
  • It then converts the response entity to a string.
  • This string (presumably JSON or some other format representing recipes) is logged at the debug level and then returned.
  • The try-with-resources statement is used to ensure that the CloseableHttpClient is properly closed after the operation is complete.
  • If an IOException occurs, an error is logged, and null is returned. This exception might occur if there’s a problem with the network connection, the server, or the process of reading the response.

Step 3: Creating the Servlet

Servlet Implementation: Create a servlet that uses the OSGi service to get the recipe data.

package com.adobe.aem.guides.wknd.core.servlets;

import com.adobe.aem.guides.wknd.core.services.RecipeService;
import org.apache.sling.api.servlets.HttpConstants;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.Component;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component(
        service = Servlet.class,
        property = {
                "sling.servlet.methods=" + HttpConstants.METHOD_GET,
                "sling.servlet.paths=" + "/bin/recipes"
        }
)
public class RecipeServlet extends HttpServlet {

    @Reference
    private RecipeService recipeService;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("application/json");
        resp.getWriter().write(recipeService.getRecipes());
    }
}

Servlet Annotation (@Component)

  • The @Component annotation is used to declare this class as an OSGi component. It specifies that this component serves as a Servlet.
  • The properties:
    • "sling.servlet.methods=" + HttpConstants.METHOD_GET indicates that this servlet will handle HTTP GET requests.
    • "sling.servlet.paths=" + "/bin/recipes" sets the path at which this servlet is accessible, i.e., requests to /bin/recipes will be handled by this servlet.

Class Declaration

  • public class RecipeServlet extends HttpServlet
    • RecipeServlet is a public class that extends HttpServlet, making it a servlet capable of handling HTTP requests.

Service Reference (@Reference)

  • private RecipeService recipeService;
    • This is a reference to the RecipeService, annotated with @Reference, which is an OSGi way of dependency injection. The OSGi framework will automatically inject an instance of RecipeService when RecipeServlet is instantiated.

Overridden doGet Method

  • The doGet method is overridden from HttpServlet to provide specific functionality for HTTP GET requests.
  • resp.setContentType("application/json"); sets the content type of the response to JSON, which is appropriate for API responses delivering recipe data.
  • resp.getWriter().write(recipeService.getRecipes()); writes the string returned by recipeService.getRecipes() to the response. This string is presumably JSON-formatted data containing recipes.

Step 4: Testing with Postman

  1. Deploy Your Code: Deploy the above code to your AEM instance.
  2. Testing with Postman: Open Postman and create a new GET request to http://localhost:4502/bin/recipes. Replace localhost:4502 with your AEM instance URL if different. Send the request, and you should receive the JSON response from the external API.

Conclusion: This article covered creating an OSGi service in AEM to fetch data from an external API, integrating this service with a servlet, and testing the end-to-end functionality using Postman. This approach is useful for integrating third-party APIs into AEM projects.