April 7, 2020
Estimated Post Reading Time ~

News Component using Guardian News API in AEM



A simple news component that used Guardian News API and reduces Servlet data calls using the browser's local storage features.

Here’s the overview of component:-
The component displays 4 news for each of the keyword authored. As an input to the component author has to give four keywords for which he wants the news to be displayed. These keywords can be any like Apple, Adobe, Facebook, Google, etc.

Once inputs are given Servlet will fetch the news and returned to client-side JavaScript. It’s the responsibility of JavaScript to make sure it caches this news data in browser local storage. There is very little chance of changing news data every hour which is why this data is cached for 1 hour in local storage. Once the time expires, again call to Guardian API will be made by the servlet and cached into local storage.

Developers can modify this cached time depending on their preference.

Following things are included in our news component:

  1. A Servlet: Fetches the news using Guardian API based on the keywords authored in the component
  2. Client-side JavaScript script: It places the response received from Servlet into the page and also manages the cache of news to reduce frequent Servlet calls.
  3. Bootstrap CSS and JS
Let's get started.

1. Authoring the component
The user has to enter 4 keywords for which he wants to display news.

2. JavaScript calls to Servlet asking for news for above-authored keywords



$(document).ready(function() {
 var tags = document.getElementById('news-tags').innerHTML;
 $('[data-toggle="tooltip"]').tooltip();

 if (typeof(Storage) !== "undefined") {
  // Store
  var news = localStorage.getItem("news");
  var time = localStorage.getItem("time");
  var lastTags = localStorage.getItem("tags");
  time = new Date(time);
  var currentDate = new Date();

  if (news != null && news.length > 0 && time != null && currentDate - time < 3600000 && lastTags == tags) {
   // Retrieve data from localstorage
   document.getElementById("news-data").innerHTML = localStorage.getItem("news");
  } else if (tags.length > 5) {

   $.ajax({
    url: "/bin/getMorningNews?tags=" + tags,
    success: function(result) {

     if (result.length == 0) {
      $("#news-data").html("<div class=\"alert alert-danger\"><strong>Oops!..</strong> Something went wrong, looks like no news today so please have ice-cream<span class=\"glyphicon glyphicon-ice-lolly-tasted\"></span></div>");
     } else {
      $("#news-data").html(result);
      localStorage.setItem("news", result);
      localStorage.setItem("time", new Date());
      localStorage.setItem("tags", tags);
     }
    },
    error: function(xhr, status, error) {
     $("#news-data").html("<div class=\"alert alert-danger\"><strong>Oops!..</strong> Something went wrong, looks like no news today so please have ice-cream<span class=\"glyphicon glyphicon-ice-lolly-tasted\"></span></div>");
    }

   });
  }

 } else {
  $.ajax({
   url: "/bin/getMorningNews?tags=" + tags,
   success: function(result) {

    if (result.length == 0) {
     $("#news-data").html("<div class=\"alert alert-danger\"><strong>Oops!..</strong> Something went wrong, looks like no news today so please have ice-cream<span class=\"glyphicon glyphicon-ice-lolly-tasted\"></span></div>");
    } else {
     $("#news-data").html(result);
    }
   },
   error: function(xhr, status, error) {
    $("#news-data").html("<div class=\"alert alert-danger\"><strong>Oops!..</strong> Something went wrong, looks like no news today so please have ice-cream<span class=\"glyphicon glyphicon-ice-lolly-tasted\"></span></div>");
   }

  });
 }
});

This code will take care of all local storage and its time out, below Servlet will be called by it.

3. Servlet which fetches news from Guardian API


package com.aemclub.newscomponent.core.servlets;

import org.apache.felix.scr.annotations.sling.SlingServlet;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.servlets.SlingAllMethodsServlet;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import javax.servlet.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.nio.charset.Charset;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author praveen
 * Servlet that writes some sample content into the response. It is mounted for
 * all resources of a specific Sling resource type. The
 * {@link SlingSafeMethodsServlet} shall be used for HTTP methods that are
 * idempotent. For write operations use the {@link SlingAllMethodsServlet}.
 */
@SuppressWarnings("serial")
@SlingServlet(paths = "/bin/getMorningNews", methods = "GET")
public class SimpleServlet extends SlingSafeMethodsServlet {
private static final Logger LOGGER = LoggerFactory
.getLogger(SimpleServlet.class);

@Override
protected void doGet(final SlingHttpServletRequest req,
final SlingHttpServletResponse resp) throws ServletException,
IOException {

try {
final String guardianNewsAPI = "http://content.guardianapis.com/search?api-key=test&page-size=4&q=";
JSONArray completeJson = new JSONArray();
String paramValue = req.getParameter("tags");
LOGGER.info("paramValue:" + paramValue);
String[] allTags = paramValue.split("\\|");

JSONObject appleJson = readJsonFromUrl(guardianNewsAPI + allTags[0]);
JSONObject globeJson = readJsonFromUrl(guardianNewsAPI + allTags[1]);
JSONObject googleJson = readJsonFromUrl(guardianNewsAPI
+ allTags[2]);
JSONObject moneyJson = readJsonFromUrl(guardianNewsAPI + allTags[3]);

completeJson.put(appleJson);
completeJson.put(globeJson);
completeJson.put(googleJson);
completeJson.put(moneyJson);

String[] topics = { allTags[0], allTags[1], allTags[2], allTags[3] };

String html = "", active = "";
String[] dataHtml = new String[4];

for (int i = 0; i < 4; i++) {

// Adding 'active' class to the html of first tab
if (i == 0)
active = "active";
else
active = "";

JSONObject x = completeJson.getJSONObject(i);
JSONObject response = x.getJSONObject("response"); // Getting
// response
// object
JSONArray res = new JSONArray();
res = response.getJSONArray("results"); // Reading results

// Generating HTML to be returned to client side
dataHtml[i] = "<div role=\"tabpanel\" class=\"tab-pane "
+ active + "\" id=\"" + topics[i] + "\">"
+ "<ul class=\"list-group\">";
for (int k = 0; k < res.length(); k++) {
JSONObject p = res.getJSONObject(k);
String data = "<li class=\"list-group-item\"><span class=\"glyphicon glyphicon-pushpin\">"
+ "</span> <span class=\"label label-info\">"
+ p.get("sectionId")
+ "</span> "
+ "<a target=\"_blank\" href = \""
+ p.get("webUrl")
+ "\"> "
+ p.get("webTitle")
+ "</a> </li>";

dataHtml[i] = dataHtml[i] + data;
}

dataHtml[i] = dataHtml[i] + "</ul></div>";
html = html + dataHtml[i];
}
resp.setContentType("text/html");
resp.getWriter().write(html);
} catch (Exception e) {

LOGGER.info("Something went wrong:" + e.getMessage());
}
}

private static String readAll(Reader rd) throws IOException {
StringBuilder sb = new StringBuilder();
int cp;
while ((cp = rd.read()) != -1) {
sb.append((char) cp);
}
return sb.toString();
}

public static JSONObject readJsonFromUrl(String url) throws IOException,
JSONException {
InputStream is = new URL(url).openStream();
try {
BufferedReader rd = new BufferedReader(new InputStreamReader(is,
Charset.forName("UTF-8")));
String jsonText = readAll(rd);
JSONObject json = new JSONObject(jsonText);
return json;
} finally {
is.close();
}
}
}

4. Once data is returned from client-side, it can be seen on component


A simple error will appear if there is something broken due to Internet connection

Feel free to enhance and optimize this component. The complete project is available on our Github repository.


By aem4beginner

No comments:

Post a Comment

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