grsf-publisher-ws/src/main/java/org/gcube/data_catalogue/grsf_publish_ws/utils/HelperMethods.java

492 lines
16 KiB
Java

package org.gcube.data_catalogue.grsf_publish_ws.utils;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletContext;
import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.CkanResource;
import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.CustomField;
import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Group;
import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.Tag;
import org.gcube.data_catalogue.grsf_publish_ws.json.input.Common;
import org.gcube.data_catalogue.grsf_publish_ws.json.input.Resource;
import org.gcube.data_catalogue.grsf_publish_ws.json.input.TimeSeriesBean;
import org.gcube.data_catalogue.grsf_publish_ws.utils.groups.Status;
import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue;
import org.gcube.datacatalogue.ckanutillibrary.DataCatalogueFactory;
import org.gcube.datacatalogue.ckanutillibrary.DataCatalogueImpl;
import org.gcube.datacatalogue.ckanutillibrary.models.ResourceBean;
import org.slf4j.LoggerFactory;
import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpResponse;
import eu.trentorise.opendata.jackan.internal.org.apache.http.client.methods.HttpGet;
import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.CloseableHttpClient;
import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.HttpClientBuilder;
import eu.trentorise.opendata.jackan.model.CkanLicense;
/**
* Helper methods
* @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
*/
public abstract class HelperMethods {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(HelperMethods.class);
// to be retrieved from the web.xml
private static final String PENDING_CONTEX_KEY = "PendingContext";
private static final String CONFIRMED_CONTEX_KEY = "ConfirmedContext";
private static final String CSV_FILE_FORMAT = ".csv";
private static final int TIME_SERIES_TAKE_LAST_VALUES = 5;
/**
* Convert a group name to its id on ckan
* @param origName
* @return
*/
public static String getGroupNameOnCkan(String origName){
if(origName == null)
throw new IllegalArgumentException("origName cannot be null");
String modified = origName.trim().toLowerCase().replaceAll("[^A-Za-z0-9-]", "-");
if(modified.startsWith("-"))
modified = modified.substring(1);
if(modified.endsWith("-"))
modified = modified.substring(0, modified.length() -1);
return modified;
}
/**
* Retrieve the running instance of the data catalogue for this scope
* @return
* @throws Exception
*/
public static DataCatalogue getDataCatalogueRunningInstance(String scope){
try{
DataCatalogueImpl instance = DataCatalogueFactory.getFactory().getUtilsPerScope(scope);
return instance;
}catch(Exception e){
logger.error("Failed to instanciate data catalogue lib", e);
}
return null;
}
/**
* Retrieve the list of tags for this object
*/
public static void getTags(List<String> tags, Common record){
Class<?> current = record.getClass();
do{
Field[] fields = current.getDeclaredFields();
for (Field field : fields) {
if(field.isAnnotationPresent(Tag.class)){
try{
Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record);
if(f != null){
if(f instanceof List<?>){
List asList = ((List) f);
if(!asList.isEmpty()){
logger.debug("The object annotated with @Tag is a list. Adding ... ");
int elementsToConsider = asList.size();
// check if it is a time series, in this take the last X elements
if(asList.get(0).getClass().equals(TimeSeriesBean.class)){
elementsToConsider = Math.min(elementsToConsider, TIME_SERIES_TAKE_LAST_VALUES);
for (int i = (asList.size() - elementsToConsider); i < asList.size(); i++) {
logger.debug(asList.get(i).toString().trim());
tags.add(asList.get(i).toString().trim());
}
}else{
// else add all the available elements
for (int i = 0; i < elementsToConsider; i++) {
logger.debug(asList.get(i).toString().trim());
tags.add(asList.get(i).toString().trim());
}
}
}
}else{
logger.debug("The object annotated with @Tag is a simple one. Adding ... ");
logger.debug(f.toString().trim());
tags.add(f.toString().trim());
}
}
}catch(Exception e){
logger.error("Failed ot read value for field " + field.getName() + " skipping", e);
}
}
}
}
while((current = current.getSuperclass())!=null); // start from the inherited class up to the Object.class
logger.info("Tags are " + tags);
}
/**
* Retrieve the list of groups' names for this object
*/
public static void getGroups(List<String> groups, Common record){
Class<?> current = record.getClass();
do{
Field[] fields = current.getDeclaredFields();
for (Field field : fields) {
if(field.isAnnotationPresent(Group.class)){
try{
Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record);
if(f != null){
// also convert to the group name that should be on ckan
String groupName = getGroupNameOnCkan(f.toString().trim());
if(!groups.contains(groupName))
groups.add(groupName);
}
}catch(Exception e){
logger.error("Failed ot read value for field " + field.getName() + " skipping", e);
}
}
}
}
while((current = current.getSuperclass())!=null); // start from the inherited class up to the Object.class
logger.info("Groups is " + groups);
}
/**
* Retrieve the list of extras for this object
*/
public static void getExtras(Map<String, List<String>> extras, Common record){
Class<?> current = record.getClass();
do{
Field[] fields = current.getDeclaredFields();
for (Field field : fields) {
if(field.isAnnotationPresent(CustomField.class)){
try{
Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record);
String keyField = field.getAnnotation(CustomField.class).key();
if(f != null){
List<String> valuesForKey = new ArrayList<String>();
// check if the map already contains this key
if(extras.containsKey(keyField))
valuesForKey = extras.get(keyField);
if(f instanceof List<?>){
logger.debug("The object " + field.getName() + " is a list and is annotated with @CustomField. Adding ...");
List asList = (List)f;
if(!asList.isEmpty()){
int elementsToConsider = asList.size();
// check if it is a time series, in this case take the last X elements
if(asList.get(0).getClass().equals(TimeSeriesBean.class)){
elementsToConsider = Math.min(elementsToConsider, TIME_SERIES_TAKE_LAST_VALUES);
for (int i = (asList.size() - elementsToConsider); i < asList.size(); i++) {
logger.debug(asList.get(i).toString().trim());
valuesForKey.add(asList.get(i).toString().trim());
}
}else{
for (int i = 0; i < elementsToConsider; i++) {
logger.debug(asList.get(i).toString().trim());
valuesForKey.add(asList.get(i).toString().trim());
}
}
}
}else{
valuesForKey.add(f.toString().trim());
}
// add to the map
extras.put(keyField, valuesForKey);
}
}catch(Exception e){
logger.error("Failed ot read value for field " + field.getName() + " skipping", e);
}
}
}
}
while((current = current.getSuperclass())!=null); // start from the inherited class up to the Object.class
logger.info("Extras is " + extras);
}
/**
* Retrieve the organization name in which the user wants to publish starting from the scope
* @param contextInWhichPublish
* @return
*/
public static String retrieveOrgNameFromScope(String scope) {
String[] splittedScope = scope.split("/");
return splittedScope[splittedScope.length - 1].toLowerCase();
}
/**
* Return the context in which the user wants to publish by the status information
* @param status
* @param contextServlet
* @return
*/
public static String getContextFromStatus(Status status, ServletContext contextServlet) {
String toReturn = null;
switch(status){
case Confirmed :
toReturn = (String)contextServlet.getInitParameter(CONFIRMED_CONTEX_KEY);
break;
case Pending:
toReturn = (String)contextServlet.getInitParameter(PENDING_CONTEX_KEY);
break;
default: break;
}
logger.debug("Context evaluated is " + toReturn);
return toReturn;
}
/**
* Validate the name the product will have
* @param futureName
* @return
*/
public static boolean isNameValid(String futureName) {
if(futureName == null || futureName.isEmpty())
return false;
else{
return futureName.matches("[\\sA-Za-z0-9_.-]+");
}
}
/**
* Retrieve the user's email given his/her username
* @param context
* @param token
* @return
* @throws Exception
*/
public static String getUserEmail(String context, String token){
String baseUrl = new ServiceEndPointReaderSocial(context).getBasePath();
String url = baseUrl + "users/getUserEmail?gcube-token=" + token;
logger.debug("Request url is " + url);
return executGETHttpRequest(url, 200);
}
/**
* Retrieve the user's fullname given his/her username
* @param context
* @param token
* @return
* @throws Exception
*/
public static String getUserFullname(String context, String token){
String baseUrl = new ServiceEndPointReaderSocial(context).getBasePath();
String url = baseUrl + "users/getUserFullname?gcube-token=" + token;
logger.debug("Request url is " + url);
return executGETHttpRequest(url, 200);
}
/**
* Execute the GET http request at this url, and return the result as string
* @return
*/
private static String executGETHttpRequest(String url, int expectedCodeOnSuccess){
try(CloseableHttpClient client = HttpClientBuilder.create().build();){
HttpGet getRequest = new HttpGet(url);
HttpResponse response = client.execute(getRequest);
if (response.getStatusLine().getStatusCode() != expectedCodeOnSuccess) {
throw new RuntimeException("Failed : HTTP error code : "
+ response.getStatusLine().getStatusCode());
}
BufferedReader br = new BufferedReader(
new InputStreamReader((response.getEntity().getContent())));
String res = "";
String temp = null;
while ((temp = br.readLine()) != null) {
res += temp;
}
return res;
}catch(Exception e){
logger.error("error while performing post method " + e.toString());
}
return null;
}
/**
* Retrieve the list of ckan licenses and build up a map <license_id, license_title>
* @return
* @throws Exception
*/
public static Map<String, String> getLicenses(DataCatalogue catalogue) throws Exception {
Map<String, String> toReturn = new HashMap<String, String>();
List<CkanLicense> licenses = catalogue.getLicenses();
for (CkanLicense ckanLicense : licenses) {
toReturn.put(ckanLicense.getId(), ckanLicense.getTitle());
}
return toReturn;
}
/**
* Check that the given license id is in CKAN
* @param license id to check
* @return
* @throws Exception
*/
public static boolean existsLicenseId(String license, DataCatalogue catalogue) throws Exception {
List<CkanLicense> licenses = catalogue.getLicenses();
for (CkanLicense ckanLicense : licenses) {
if(ckanLicense.getId().equals(license))
return true;
}
return false;
}
/**
* Retrieve the ResourceBean given the record (extract resources from Database Sources and Source of Information and others)
* @param record
* @param username
* @param tags
* @param groups
* @return
*/
public static List<ResourceBean> getResourcesFromBean(Common record, String username, List<String> tags, List<String> groups){
List<ResourceBean> toReturn = new ArrayList<ResourceBean>();
Class<?> current = record.getClass();
do{
Field[] fields = current.getDeclaredFields();
for (Field field : fields) {
if(field.isAnnotationPresent(CkanResource.class)){
try{
Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record);
if(f != null){
if(f instanceof List<?>){
List<Resource> listOfResources = (List<Resource>)f;
for (Resource resource : listOfResources) {
toReturn.add(new ResourceBean(resource.getUrl(), resource.getName().toString(), resource.getDescription(), null, username, null, null));
}
}else{
Resource res = (Resource)f;
toReturn.add(new ResourceBean(res.getUrl(), res.getName().toString(), res.getDescription(), null, username, null, null));
}
}
}catch(Exception e){
logger.error("Failed ot read value for field " + field.getName() + " skipping", e);
}
}
}
}
while((current = current.getSuperclass())!=null); // iterate from the inherited class up to the Object.class
logger.info("Returning resources " + toReturn);
return toReturn;
}
/**
* Manage the time series bean within a resource (e.g., catches or landings, exploitation rate and so on).
* The method save the time series as csv on ckan, and also save the file in the .catalogue area of the shared vre folder.
* @param record
* @throws IntrospectionException
* @throws InvocationTargetException
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static void manageTimeSeries(Common record, String packageId, String username, DataCatalogue catalogue, String context) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException{
if(record == null)
throw new IllegalArgumentException("The given record is null!!");
Class<?> current = record.getClass();
do{
Field[] fields = current.getDeclaredFields();
for (Field field : fields) {
if (Collection.class.isAssignableFrom(field.getType())) {
// if the list is not null, get an element
Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record);
if(f != null){
List asList = (List)f;
if(!asList.isEmpty()){
if(asList.get(0).getClass().equals(TimeSeriesBean.class)){
CustomField customAnnotation = field.getAnnotation(CustomField.class);
String resourceToAttachName = customAnnotation.key().replaceAll("\\s", "_") + CSV_FILE_FORMAT;
logger.debug("A time series has been just found");
File csvFile = CSVHelpers.listToCSV(asList);
if(csvFile != null){
// upload this file on ckan
eu.trentorise.opendata.jackan.model.CkanResource res = catalogue.uploadResourceFile(
csvFile,
packageId,
catalogue.getApiKeyFromUsername(username),
resourceToAttachName,
customAnnotation.key() + " time series for this product");
logger.debug("The resource returned is " + res.getName() + " with package id " + res.getPackageId() + " " + res.getDescription() + " " + res.getState());
// upload this file on the folder of the vre (under .catalogue) and change the url of the resource TODO
// if(res != null){
//
//
// }
}
}
}
}
}
}
}
while((current = current.getSuperclass())!=null); // iterate from the inherited class up to the Object.class
}
}