/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.dynamic.data.mapping.data.provider.internal.rest;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import com.liferay.dynamic.data.mapping.data.provider.DDMDataProvider;
import com.liferay.dynamic.data.mapping.data.provider.DDMDataProviderContext;
import com.liferay.dynamic.data.mapping.data.provider.DDMDataProviderException;
import com.liferay.dynamic.data.mapping.data.provider.DDMDataProviderOutputParametersSettings;
import com.liferay.dynamic.data.mapping.data.provider.DDMDataProviderRequest;
import com.liferay.dynamic.data.mapping.data.provider.DDMDataProviderResponse;
import com.liferay.dynamic.data.mapping.data.provider.DDMDataProviderResponse.Status;
import com.liferay.dynamic.data.mapping.data.provider.DDMDataProviderResponseOutput;
import com.liferay.portal.kernel.cache.MultiVMPool;
import com.liferay.portal.kernel.cache.PortalCache;
import com.liferay.portal.kernel.json.JSONFactory;
import com.liferay.portal.kernel.util.CharPool;
import com.liferay.portal.kernel.util.KeyValuePair;
import com.liferay.portal.kernel.util.ListUtil;
import com.liferay.portal.kernel.util.StringPool;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import java.io.Serializable;
import java.net.ConnectException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import jodd.http.HttpException;
import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
/**
* @author Marcellus Tavares
*/
@Component(immediate = true, property = "ddm.data.provider.type=rest")
public class DDMRESTDataProvider implements DDMDataProvider {
@Override
public List<KeyValuePair> getData(
DDMDataProviderContext ddmDataProviderContext)
throws DDMDataProviderException {
try {
DDMDataProviderRequest ddmDataProviderRequest =
createDDMDataProviderRequest(ddmDataProviderContext);
DDMDataProviderResponse ddmDataProviderResponse = doGetData(
ddmDataProviderRequest);
DDMDataProviderResponseOutput ddmDataProviderResponseOutput =
ddmDataProviderResponse.get("Default-Output");
List<KeyValuePair> results = ddmDataProviderResponseOutput.getValue(
List.class);
return results;
}
catch (Exception e) {
throw new DDMDataProviderException(e);
}
}
@Override
public DDMDataProviderResponse getData(
DDMDataProviderRequest ddmDataProviderRequest)
throws DDMDataProviderException {
try {
return doGetData(ddmDataProviderRequest);
}
catch (HttpException he) {
Throwable cause = he.getCause();
if (cause instanceof ConnectException) {
return DDMDataProviderResponse.error(
Status.SERVICE_UNAVAILABLE);
}
else {
throw new DDMDataProviderException(he);
}
}
catch (Exception e) {
throw new DDMDataProviderException(e);
}
}
@Override
public Class<?> getSettings() {
return DDMRESTDataProviderSettings.class;
}
protected DDMDataProviderRequest createDDMDataProviderRequest(
DDMDataProviderContext ddmDataProviderContext) {
DDMDataProviderRequest ddmDataProviderRequest =
new DDMDataProviderRequest(null, null);
ddmDataProviderRequest.setDDMDataProviderContext(
ddmDataProviderContext);
// Backwards compatibility
ddmDataProviderRequest.queryString(
ddmDataProviderContext.getParameters());
return ddmDataProviderRequest;
}
protected DDMDataProviderResponse createDDMDataProviderResponse(
DocumentContext documentContext,
DDMDataProviderRequest ddmDataProviderRequest,
DDMRESTDataProviderSettings ddmRESTDataProviderSettings) {
DDMDataProviderOutputParametersSettings[] outputParameterSettingsArray =
ddmRESTDataProviderSettings.outputParameters();
if ((outputParameterSettingsArray == null) ||
(outputParameterSettingsArray.length == 0)) {
return DDMDataProviderResponse.of();
}
List<DDMDataProviderResponseOutput> ddmDataProviderResponseOutputs =
new ArrayList<>();
for (DDMDataProviderOutputParametersSettings outputParameterSettings :
outputParameterSettingsArray) {
String name = outputParameterSettings.outputParameterName();
String type = outputParameterSettings.outputParameterType();
String path = outputParameterSettings.outputParameterPath();
if (Objects.equals(type, "text")) {
String nomalizedPath = normalizePath(path);
ddmDataProviderResponseOutputs.add(
DDMDataProviderResponseOutput.of(
name, "text", documentContext.read(nomalizedPath)));
}
else if (Objects.equals(type, "number")) {
String nomalizedPath = normalizePath(path);
ddmDataProviderResponseOutputs.add(
DDMDataProviderResponseOutput.of(
name, "number", documentContext.read(nomalizedPath)));
}
else if (Objects.equals(type, "list")) {
String[] paths = StringUtil.split(path, CharPool.SEMICOLON);
String normalizedValuePath = normalizePath(paths[0]);
String normalizedKeyPath = normalizedValuePath;
List<String> values = documentContext.read(normalizedValuePath);
List<String> keys = new ArrayList<>(values);
if (paths.length >= 2) {
normalizedKeyPath = normalizePath(paths[1]);
keys = documentContext.read(normalizedKeyPath);
}
List<KeyValuePair> keyValuePairs = new ArrayList<>();
for (int i = 0; i < values.size(); i++) {
keyValuePairs.add(
new KeyValuePair(keys.get(i), values.get(i)));
}
if (ddmRESTDataProviderSettings.pagination()) {
int start = Integer.valueOf(
ddmDataProviderRequest.getParameter("paginationStart"));
int end = Integer.valueOf(
ddmDataProviderRequest.getParameter("paginationEnd"));
if (keyValuePairs.size() > (end - start)) {
keyValuePairs = ListUtil.subList(
keyValuePairs, start, end);
}
}
ddmDataProviderResponseOutputs.add(
DDMDataProviderResponseOutput.of(
name, "list", keyValuePairs));
}
}
int size = ddmDataProviderResponseOutputs.size();
return DDMDataProviderResponse.of(
ddmDataProviderResponseOutputs.toArray(
new DDMDataProviderResponseOutput[size]));
}
protected DDMDataProviderResponse doGetData(
DDMDataProviderRequest ddmDataProviderRequest) {
DDMDataProviderContext ddmDataProviderContext =
ddmDataProviderRequest.getDDMDataProviderContext();
DDMRESTDataProviderSettings ddmRESTDataProviderSettings =
ddmDataProviderContext.getSettingsInstance(
DDMRESTDataProviderSettings.class);
HttpRequest httpRequest = HttpRequest.get(
ddmRESTDataProviderSettings.url());
if (Validator.isNotNull(ddmRESTDataProviderSettings.username())) {
httpRequest.basicAuthentication(
ddmRESTDataProviderSettings.username(),
ddmRESTDataProviderSettings.password());
}
setRequestParameters(
ddmDataProviderRequest, ddmRESTDataProviderSettings, httpRequest);
String cacheKey = getCacheKey(httpRequest);
DDMRESTDataProviderResult ddmRESTDataProviderResult = _portalCache.get(
cacheKey);
if ((ddmRESTDataProviderResult != null) &&
ddmRESTDataProviderSettings.cacheable()) {
return ddmRESTDataProviderResult.getDDMDataProviderResponse();
}
HttpResponse httpResponse = httpRequest.send();
DocumentContext documentContext = JsonPath.parse(httpResponse.body());
DDMDataProviderResponse ddmDataProviderResponse =
createDDMDataProviderResponse(
documentContext, ddmDataProviderRequest,
ddmRESTDataProviderSettings);
if (ddmRESTDataProviderSettings.cacheable()) {
_portalCache.put(
cacheKey,
new DDMRESTDataProviderResult(ddmDataProviderResponse));
}
return ddmDataProviderResponse;
}
protected String getCacheKey(HttpRequest httpRequest) {
return httpRequest.url();
}
protected String normalizePath(String path) {
if (StringUtil.startsWith(path, StringPool.PERIOD) ||
StringUtil.startsWith(path, StringPool.DOLLAR)) {
return path;
}
return StringPool.PERIOD.concat(path);
}
@Reference(unbind = "-")
protected void setJSONFactory(JSONFactory jsonFactory) {
_jsonFactory = jsonFactory;
}
@Reference(unbind = "-")
protected void setMultiVMPool(MultiVMPool multiVMPool) {
_portalCache =
(PortalCache<String, DDMRESTDataProviderResult>)
multiVMPool.getPortalCache(DDMRESTDataProvider.class.getName());
}
protected void setRequestParameters(
DDMDataProviderRequest ddmDataProviderRequest,
DDMRESTDataProviderSettings ddmRESTDataProviderSettings,
HttpRequest httpRequest) {
if (ddmRESTDataProviderSettings.filterable()) {
httpRequest.query(
ddmRESTDataProviderSettings.filterParameterName(),
ddmDataProviderRequest.getParameter("filterParameterValue"));
}
if (ddmRESTDataProviderSettings.pagination()) {
httpRequest.query(
ddmRESTDataProviderSettings.paginationEndParameterName(),
ddmDataProviderRequest.getParameter("paginationStart"));
httpRequest.query(
ddmRESTDataProviderSettings.paginationEndParameterName(),
ddmDataProviderRequest.getParameter("paginationEnd"));
}
httpRequest.query(ddmDataProviderRequest.getParameters());
}
private JSONFactory _jsonFactory;
private PortalCache<String, DDMRESTDataProviderResult> _portalCache;
private static class DDMRESTDataProviderResult implements Serializable {
public DDMRESTDataProviderResult(
DDMDataProviderResponse ddmDataProviderResponse) {
_ddmDataProviderResponse = ddmDataProviderResponse;
}
public DDMDataProviderResponse getDDMDataProviderResponse() {
return _ddmDataProviderResponse;
}
private final DDMDataProviderResponse _ddmDataProviderResponse;
}
}