This article will discuss the process of creating a servlet in AEM and then demonstrate how to execute this servlet from external locations such as Postman or React components.
Request Flow
first of all we need to understand the request flow.
- React will to AEM for Data
- AEM Request to MicroServices
- MicroServices will Response to AEM
- AEM will Response to React.
Create Servlet File
- Goto yourProject > core > src > main > java > sa > com > stc > core > servlet
- Start by creating a new servlet file, such as "ExternalApiServlet.java."
- In this servlet, we'll implement our business logic to make API calls.
- The servlet code is provided below, with important sections explained in comments.
ExternalApiServlet.java
/*
* Import Important NameSpaces
*/
package sa.com.stc.core.servlets;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.osgi.service.component.annotations.Component;
import com.google.gson.Gson;
import javax.servlet.Servlet;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sa.com.stc.core.utilities.RequestHandler;
import sa.com.stc.core.utilities.ApisUrlConstant;
/*
* We establish the servlet path, which will be used to execute the servlet in Postman.
* We define the servlet method, and we will use the same method to execute it from
external sources.
*/
@Component(service = { Servlet.class }, property = {
"sling.servlet.paths=/bin/external-api",
"sling.servlet.methods=GET"
})
public class ExternalApiServlet extends SlingSafeMethodsServlet {
/*
* I have created a separate RequestHandler class and instantiated an object for it.
*/
RequestHandler requestHandler = new RequestHandler();
/*
* Create object of logger class for logs
*/
private static final Logger LOGGER =
LoggerFactory.getLogger(ExternalApiServlet.class);
/*
* doGet Method used to call get type Api
*/
@Override
protected void doGet(SlingHttpServletRequest request,
SlingHttpServletResponse response) throws IOException {
LOGGER.info("ServletCalled");
try {
Gson gson = new Gson();
/*
* getServletCallRequestBody function is managing some Constant and Query Parms
*/
String resuestBody=requestHandler.getServletCallRequestBody(request);
/*
* fetchRequestResponse function is calling api
*/
String jsonResponse = gson.toJson(requestHandler.fetchRequestResponse
(request, ApisUrlConstant.FETCH_EBU_TICKET_TYPES));
/*
* returning API Response
*/
response.getWriter().write(resuestBody == null ? jsonResponse : resuestBody);
} catch (Exception e) {
Gson gson = new Gson();
String jsonResponse = gson.toJson(e);
response.getWriter().write(jsonResponse);
}
}
}
Custom Class RequestHandler.java
This class have some custom functions for managing planeString or Hashing
package sa.com.stc.core.utilities;
import java.util.Date;
import java.text.SimpleDateFormat;
// Hasing
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.sling.api.SlingHttpServletRequest;
public class RequestHandler {
String agent = null;
String appVersion = null;
String client = null;
String secretKey = null;
String lang = null;
String version = null;
String url = null;
String resourcePath = null;
String basePath = GlobalConstant.BASE_PATH;
// MANAGE CONSTATNS FOR SLING MODEL CALL
public void setSlingModelConstant(String AGENT, String APP_VERSION,
String CLIENT, String SECRET_KEY, String LANG,
String VERSION, String RESOUCE_PATH) {
agent = AGENT;
appVersion = APP_VERSION;
client = CLIENT;
secretKey = SECRET_KEY;
lang = LANG;
version = VERSION;
resourcePath = RESOUCE_PATH;
}
// SERVLET CALL REQUEST BODY RESOLVED;
public String getServletCallRequestBody(SlingHttpServletRequest request) {
try {
agent = GlobalConstant.AGENT;
appVersion = GlobalConstant.APP_VERSION;
client = GlobalConstant.CLIENT;
secretKey = GlobalConstant.SECRET_KEY;
resourcePath = request.getParameter("resourcePath");
lang = request.getParameter("lang");
version = request.getParameter("version");
return (resourcePath == null || resourcePath.isEmpty() || lang == null ||
lang.isEmpty() || version == null
|| version.isEmpty()) ? "lang,version and resourcePath is required" : null;
} catch (Exception e) {
return "Error";
}
}
// GET CURRENT TIME
public String currentTimeString() {
Date currentTime = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
dateFormat.setTimeZone(java.util.TimeZone.getTimeZone("GMT"));
String currentTimeString = dateFormat.format(currentTime);
return currentTimeString;
}
// Resolve Request
public void resolvedRequest(String fullPath) {
String[] resolvedRequest = fullPath.split("/resources/");
url = "/" + resolvedRequest[1];
}
// PLAN STRING GENRATOR
public String PlainStringToHash(String httpMethod, String fullPath) {
resolvedRequest(fullPath);
String plainStringToHash = httpMethod + url + lang + currentTimeString() +
agent + version + appVersion + client + httpMethod;
return plainStringToHash;
}
// HASH GERNRATOR FOR HEADER
public String hashGenrator(String httpMethod, String fullPath) {
String plainStringToHash = PlainStringToHash(httpMethod, fullPath);
String hash = null;
String secretKeys = secretKey; // Secret key
try {
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKeys.getBytes
(StandardCharsets.UTF_8), "HmacSHA256");
try {
hmacSha256.init(secretKeySpec);
byte[] hashBytes = hmacSha256.doFinal(plainStringToHash.getBytes
(StandardCharsets.UTF_8));
String hashBase64 = Base64.getEncoder().encodeToString(hashBytes);
hash = hashBase64 + "~" + agent + "~" + appVersion;
} catch (InvalidKeyException e) {
e.printStackTrace();
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return hash;
}
// Call Api
public String fetchRequestResponse(SlingHttpServletRequest request, String costantApisUrl) {
String httpMethod = request.getMethod(); // GET REQUEST METHOD
String fullPath = basePath + resourcePath + "/" + version + "/" + lang
+ costantApisUrl;
// CALLING API WITH HEADERS
try {
HttpClient httpClient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(fullPath);
httpGet.setHeader("Cookie",
"2ac066e4fab68d5d7fe73dc2d44aac44=66152fdf70ae6129e08e3884a9ac4d81");
httpGet.setHeader("X-STC-API-Key", GlobalConstant.CLIENT);
httpGet.setHeader("EDA-CLIENT", hashGenrator(httpMethod, fullPath));
httpGet.setHeader("EDA-DATE", currentTimeString());
HttpResponse responsed = httpClient.execute(httpGet);
if (responsed.getStatusLine().getStatusCode() == 200) {
String responseData = EntityUtils.toString(responsed.getEntity());
return responseData;
} else {
String errorMessage = "Error: " + responsed.getStatusLine().getStatusCode() + " "
+ responsed.getStatusLine().getReasonPhrase();
return errorMessage;
}
} catch (Exception e) {
return "catch error";
}
};
}
Custom class GlobalConstant.java
This class manages all the global constants that we use in both our RequestHandler class and the servlet.package sa.com.stc.core.utilities;
public class GlobalConstant {
public static final String BASE_PATH = "https://sandbox.api.stc.com.sa:9503";
public static final String AGENT = "web";
public static final String APP_VERSION = "1.0";
public static final String CLIENT = "PjQZWFkfNDDfBg6FqBYDDfSJG5qXGyK0GHMCVSO";
public static final String SECRET_KEY = "uHf5LxGmzZtYzJXHmnad95ftD8u6Amh8CAMpV";
public static final String LANG_EN = "en";
public static final String LANG_AR = "ar";
public static final String VERSION_1 = "v1";
public static final String VERSION_2 = "v2";
public static final String VERSION_3 = "v3";
}
Custom Class ApisUrlConstant.java
This class will contain all the API constants that we will use in our servlet.
package sa.com.stc.core.utilities;
public class ApisUrlConstant {
public static final String FETCH_PUBLIC_PRODUCTS = "/public/content/products";
public static final String FETCH_EBU_TICKET_TYPES =
"/public/content/strings/key/EBUTicketTypes";
}
Execute Servlet in React Component
In the following React component, you can observe how we execute the AEM Servlet within our React component.
useEffect(() => { getData(); }, []);
async function getData() { // AEM Servlet URL with query parameters const baseUrl = 'http://localhost:4502/bin/external-api'; // Query parameters const queryParams = { resourcePath: '/a-oscp/customerSelfMgmt/contentMgmt/enterprise/resources', lang: 'en', version: 'v1' }; // Headers const headers = { Authorization: 'Basic YWRtaW46YWRtaW4=', Cookie: 'cq-authoring-mode=TOUCH' };
// API URL const apiUrl = `${baseUrl}?${new URLSearchParams(queryParams)}`;
try { const response = await axios.get(apiUrl, { headers: headers }); // Handle the successful response here console.log('Response:', response.data); } catch (error:any) { // Handle errors here console.error('Error:', error.message); } }
useEffect(() => {
getData();
}, []);
async function getData() {
// AEM Servlet URL with query parameters
const baseUrl = 'http://localhost:4502/bin/external-api';
// Query parameters
const queryParams = {
resourcePath: '/a-oscp/customerSelfMgmt/contentMgmt/enterprise/resources',
lang: 'en',
version: 'v1'
};
// Headers
const headers = {
Authorization: 'Basic YWRtaW46YWRtaW4=',
Cookie: 'cq-authoring-mode=TOUCH'
};
// API URL
const apiUrl = `${baseUrl}?${new URLSearchParams(queryParams)}`;
try {
const response = await axios.get(apiUrl, {
headers: headers
});
// Handle the successful response here
console.log('Response:', response.data);
} catch (error:any) {
// Handle errors here
console.error('Error:', error.message);
}
}
Execute AEM Servlet in Postmen
In the following diagram, you can visualize the process of executing the AEM Servlet from Postman.
- To call the servlet, you need to access it through its path
- Provide three required query parameters: lang, version, and resourcePath.
Thanks
0 comments: