2016-11-24 17:53:50 +01:00
|
|
|
package org.gcube.data_catalogue.grsf_publish_ws.utils.threads;
|
|
|
|
|
2016-11-25 13:56:27 +01:00
|
|
|
import java.beans.IntrospectionException;
|
2016-11-27 12:33:41 +01:00
|
|
|
import java.beans.PropertyDescriptor;
|
|
|
|
import java.io.File;
|
|
|
|
import java.lang.reflect.Field;
|
2016-11-25 13:56:27 +01:00
|
|
|
import java.lang.reflect.InvocationTargetException;
|
2016-11-27 12:33:41 +01:00
|
|
|
import java.util.List;
|
2016-11-25 13:56:27 +01:00
|
|
|
|
2016-11-28 12:20:43 +01:00
|
|
|
import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
|
2016-11-27 12:33:41 +01:00
|
|
|
import org.gcube.common.homelibrary.home.HomeLibrary;
|
|
|
|
import org.gcube.common.homelibrary.home.exceptions.HomeNotFoundException;
|
|
|
|
import org.gcube.common.homelibrary.home.exceptions.InternalErrorException;
|
|
|
|
import org.gcube.common.homelibrary.home.exceptions.UserNotFoundException;
|
|
|
|
import org.gcube.common.homelibrary.home.workspace.Workspace;
|
|
|
|
import org.gcube.common.homelibrary.home.workspace.WorkspaceFolder;
|
|
|
|
import org.gcube.common.homelibrary.home.workspace.WorkspaceSharedFolder;
|
|
|
|
import org.gcube.common.homelibrary.home.workspace.catalogue.WorkspaceCatalogue;
|
|
|
|
import org.gcube.common.homelibrary.home.workspace.exceptions.ItemNotFoundException;
|
|
|
|
import org.gcube.common.homelibrary.home.workspace.exceptions.WorkspaceFolderNotFoundException;
|
|
|
|
import org.gcube.common.homelibrary.home.workspace.folder.items.ExternalFile;
|
2016-11-28 12:20:43 +01:00
|
|
|
import org.gcube.common.scope.api.ScopeProvider;
|
2016-11-27 12:33:41 +01:00
|
|
|
import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.CustomField;
|
2016-12-01 23:21:37 +01:00
|
|
|
import org.gcube.data_catalogue.grsf_publish_ws.custom_annotations.TimeSeries;
|
2016-12-01 22:20:46 +01:00
|
|
|
import org.gcube.data_catalogue.grsf_publish_ws.json.input.Common;
|
2016-11-27 12:33:41 +01:00
|
|
|
import org.gcube.data_catalogue.grsf_publish_ws.json.input.FisheryRecord;
|
|
|
|
import org.gcube.data_catalogue.grsf_publish_ws.json.input.StockRecord;
|
|
|
|
import org.gcube.data_catalogue.grsf_publish_ws.utils.CSVHelpers;
|
2016-11-28 17:43:28 +01:00
|
|
|
import org.gcube.data_catalogue.grsf_publish_ws.utils.HelperMethods;
|
2016-12-16 21:31:52 +01:00
|
|
|
import org.gcube.data_catalogue.grsf_publish_ws.utils.cache.CacheImpl;
|
|
|
|
import org.gcube.data_catalogue.grsf_publish_ws.utils.cache.CacheInterface;
|
2017-01-30 17:58:22 +01:00
|
|
|
import org.gcube.datacatalogue.ckanutillibrary.server.DataCatalogue;
|
2016-11-24 17:53:50 +01:00
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
2016-11-27 12:33:41 +01:00
|
|
|
import eu.trentorise.opendata.jackan.model.CkanResourceBase;
|
|
|
|
|
2016-11-24 17:53:50 +01:00
|
|
|
/**
|
|
|
|
* Extract the time series present in the record, load them as resource on ckan and on the .catalogue
|
|
|
|
* folder under the vre folder.
|
|
|
|
* @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
|
|
|
|
*/
|
|
|
|
public class ManageTimeSeriesThread extends Thread{
|
|
|
|
|
2016-11-27 12:33:41 +01:00
|
|
|
private static final String PATH_SEPARATOR = "/";
|
|
|
|
|
2016-11-24 17:53:50 +01:00
|
|
|
// Logger
|
|
|
|
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ManageTimeSeriesThread.class);
|
2016-12-03 11:50:38 +01:00
|
|
|
|
|
|
|
// try to attach the source at most CANCHES times ..
|
2016-12-01 23:21:37 +01:00
|
|
|
private static final int CANCHES = 3;
|
2016-11-24 17:53:50 +01:00
|
|
|
|
2017-07-12 18:05:35 +02:00
|
|
|
private static CacheInterface<String, WorkspaceCatalogue> vreFolderCache = new CacheImpl<String, WorkspaceCatalogue>(1000 * 60 * 60 * 24);
|
2016-12-16 21:31:52 +01:00
|
|
|
|
2016-12-01 22:20:46 +01:00
|
|
|
private Common record;
|
2017-02-23 18:55:32 +01:00
|
|
|
private String uuidKB;
|
2016-11-25 13:56:27 +01:00
|
|
|
private String username;
|
|
|
|
private DataCatalogue catalogue;
|
|
|
|
private String context;
|
2016-11-28 12:20:43 +01:00
|
|
|
private String token;
|
2016-11-25 13:56:27 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param record
|
|
|
|
* @param packageId
|
|
|
|
* @param username
|
|
|
|
* @param catalogue
|
|
|
|
* @param context
|
|
|
|
*/
|
2016-12-01 22:20:46 +01:00
|
|
|
public ManageTimeSeriesThread(Common record, String packageName,
|
2016-11-28 12:20:43 +01:00
|
|
|
String username, DataCatalogue catalogue, String context, String token) {
|
2016-11-25 13:56:27 +01:00
|
|
|
super();
|
|
|
|
this.record = record;
|
2017-02-23 18:55:32 +01:00
|
|
|
this.uuidKB = packageName;
|
2016-11-25 13:56:27 +01:00
|
|
|
this.username = username;
|
|
|
|
this.catalogue = catalogue;
|
|
|
|
this.context = context;
|
2016-11-28 12:20:43 +01:00
|
|
|
this.token = token;
|
2016-11-25 13:56:27 +01:00
|
|
|
}
|
2016-11-24 17:53:50 +01:00
|
|
|
|
2016-11-25 13:56:27 +01:00
|
|
|
@Override
|
|
|
|
public void run() {
|
2016-11-24 17:53:50 +01:00
|
|
|
logger.info("Time series manager thread started");
|
2016-11-27 12:33:41 +01:00
|
|
|
|
2016-11-28 12:20:43 +01:00
|
|
|
ScopeProvider.instance.set(context);
|
|
|
|
SecurityTokenProvider.instance.set(token);
|
|
|
|
|
2016-11-24 17:53:50 +01:00
|
|
|
try {
|
2017-02-23 18:55:32 +01:00
|
|
|
manageTimeSeries(record, uuidKB, username, catalogue);
|
2016-11-25 13:56:27 +01:00
|
|
|
logger.info("The time series manager thread ended correctly");
|
2016-11-27 12:33:41 +01:00
|
|
|
return;
|
|
|
|
} catch (IllegalAccessException e) {
|
|
|
|
logger.error("Error was " + e.getMessage());
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
logger.error("Error was " + e.getMessage());
|
|
|
|
} catch (InvocationTargetException e) {
|
|
|
|
logger.error("Error was " + e.getMessage());
|
|
|
|
} catch (WorkspaceFolderNotFoundException e) {
|
|
|
|
logger.error("Error was " + e.getMessage());
|
|
|
|
} catch (ItemNotFoundException e) {
|
|
|
|
logger.error("Error was " + e.getMessage());
|
|
|
|
} catch (IntrospectionException e) {
|
|
|
|
logger.error("Error was " + e.getMessage());
|
|
|
|
} catch (InternalErrorException e) {
|
|
|
|
logger.error("Error was " + e.getMessage());
|
|
|
|
} catch (HomeNotFoundException e) {
|
|
|
|
logger.error("Error was " + e.getMessage());
|
|
|
|
} catch (UserNotFoundException e) {
|
|
|
|
logger.error("Error was " + e.getMessage());
|
2016-11-28 12:20:43 +01:00
|
|
|
}finally{
|
|
|
|
ScopeProvider.instance.reset();
|
|
|
|
SecurityTokenProvider.instance.reset();
|
2016-11-27 12:33:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
logger.error("Failed to attach csv files to the product...");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
* @throws UserNotFoundException
|
|
|
|
* @throws HomeNotFoundException
|
|
|
|
* @throws InternalErrorException
|
|
|
|
* @throws WorkspaceFolderNotFoundException
|
|
|
|
* @throws ItemNotFoundException
|
|
|
|
*/
|
2017-02-20 18:44:26 +01:00
|
|
|
@SuppressWarnings("rawtypes")
|
2017-02-23 18:55:32 +01:00
|
|
|
public static void manageTimeSeries(Common record, String uuidKB, String username, DataCatalogue catalogue) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException, WorkspaceFolderNotFoundException, InternalErrorException, HomeNotFoundException, UserNotFoundException, ItemNotFoundException{
|
2016-11-27 12:33:41 +01:00
|
|
|
|
|
|
|
if(record == null)
|
|
|
|
throw new IllegalArgumentException("The given record is null!!");
|
|
|
|
|
2016-12-16 21:31:52 +01:00
|
|
|
String token = SecurityTokenProvider.instance.get();
|
|
|
|
|
|
|
|
WorkspaceCatalogue catalogueFolder = null;
|
|
|
|
if((catalogueFolder = vreFolderCache.get(token)) == null){
|
|
|
|
Workspace ws = HomeLibrary.getHomeManagerFactory().getHomeManager().getHome().getWorkspace();
|
|
|
|
WorkspaceSharedFolder vreFolder = ws.getVREFolderByScope(ScopeProvider.instance.get());
|
|
|
|
catalogueFolder = vreFolder.getVRECatalogue();
|
|
|
|
vreFolderCache.insert(token, catalogueFolder);
|
|
|
|
}
|
2016-11-27 12:33:41 +01:00
|
|
|
|
2016-11-28 12:20:43 +01:00
|
|
|
logger.debug("Catalogue folder in vre has path " + catalogueFolder.getPath());
|
|
|
|
|
2016-11-27 12:33:41 +01:00
|
|
|
// the structure under the .catalogue will be as follows:
|
|
|
|
// .catalogue:
|
|
|
|
// - stock:
|
2017-02-23 18:55:32 +01:00
|
|
|
// - first_letter_of_the_product (knowledge base uuid)
|
|
|
|
// - knowledge base uuid
|
2016-11-27 12:33:41 +01:00
|
|
|
// - type of files (e.g., csv)
|
2017-02-23 18:55:32 +01:00
|
|
|
// -files (e.g, kbuuid.csv)
|
2016-11-27 12:33:41 +01:00
|
|
|
// - fishery
|
2017-02-23 18:55:32 +01:00
|
|
|
// - first_letter_of_the_product (knowledge base uuid)
|
|
|
|
// - knowledge base uuid
|
2016-11-27 12:33:41 +01:00
|
|
|
// - type of files (e.g., csv)
|
2017-02-23 18:55:32 +01:00
|
|
|
// -files (e.g, kbuuid.csv)
|
2016-11-28 12:20:43 +01:00
|
|
|
|
2017-02-17 18:05:41 +01:00
|
|
|
String recordTypeFolderName = record.getGrsfType().toLowerCase();
|
2016-11-27 12:33:41 +01:00
|
|
|
String productName = record.getClass().equals(StockRecord.class) ? ((StockRecord)record).getStockName() : ((FisheryRecord)record).getFisheryName();
|
2017-02-23 18:55:32 +01:00
|
|
|
char firstLetter = uuidKB.charAt(0);
|
2016-11-27 12:33:41 +01:00
|
|
|
|
2016-11-28 12:20:43 +01:00
|
|
|
// the whole path of the directory is going to be...
|
2017-07-12 18:05:35 +02:00
|
|
|
String csvDirectoryForThisProduct = recordTypeFolderName + PATH_SEPARATOR + firstLetter + PATH_SEPARATOR + replaceIllegalChars(uuidKB) + PATH_SEPARATOR + CSVHelpers.CSV_EXTENSION.replace(".", "");
|
2016-11-28 12:20:43 +01:00
|
|
|
logger.debug("The path under which the time series are going to be saved is " + csvDirectoryForThisProduct);
|
2016-11-28 17:43:28 +01:00
|
|
|
WorkspaceFolder csvFolder = HelperMethods.createOrGetSubFoldersByPath(catalogueFolder, csvDirectoryForThisProduct);
|
2016-11-28 12:20:43 +01:00
|
|
|
|
|
|
|
if(csvFolder != null){
|
2016-11-27 12:33:41 +01:00
|
|
|
|
|
|
|
String apiKeyUser = catalogue.getApiKeyFromUsername(username);
|
|
|
|
|
|
|
|
Class<?> current = record.getClass();
|
|
|
|
do{
|
|
|
|
Field[] fields = current.getDeclaredFields();
|
|
|
|
for (Field field : fields) {
|
2016-12-01 23:21:37 +01:00
|
|
|
if (field.isAnnotationPresent(TimeSeries.class)) {
|
2017-07-13 15:43:19 +02:00
|
|
|
try{
|
|
|
|
Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record);
|
|
|
|
if(f != null){
|
2016-11-27 12:33:41 +01:00
|
|
|
|
2017-07-13 15:43:19 +02:00
|
|
|
List asList = (List)f;
|
2016-11-27 12:33:41 +01:00
|
|
|
|
2017-07-13 15:43:19 +02:00
|
|
|
if(!asList.isEmpty()){
|
2016-11-27 12:33:41 +01:00
|
|
|
|
2017-07-13 15:43:19 +02:00
|
|
|
CustomField customAnnotation = field.getAnnotation(CustomField.class);
|
|
|
|
logger.debug("A time series has been just found (from field " + customAnnotation.key() + ")");
|
|
|
|
String resourceToAttachOnCkanName = (replaceIllegalChars(productName) + "_" + customAnnotation.key()).replaceAll("\\s", "_").replaceAll("[_]+", "_") + CSVHelpers.CSV_EXTENSION;
|
|
|
|
String resourceToAttachOnCkanDescription = productName + " - " + customAnnotation.key() + " time series";
|
2016-11-27 12:33:41 +01:00
|
|
|
|
2017-07-13 15:43:19 +02:00
|
|
|
File csvFile = CSVHelpers.listToCSV(asList);
|
2016-11-27 12:33:41 +01:00
|
|
|
|
2017-07-13 15:43:19 +02:00
|
|
|
CkanResourceBase ckanResource = null;
|
|
|
|
ExternalFile createdFileOnWorkspace = null;
|
|
|
|
if(csvFile != null){
|
2016-11-27 12:33:41 +01:00
|
|
|
|
2017-07-13 15:43:19 +02:00
|
|
|
for (int i = 0; i < CANCHES; i++) {
|
2016-12-01 23:21:37 +01:00
|
|
|
|
2017-07-13 15:43:19 +02:00
|
|
|
// upload this file on ckan
|
|
|
|
if(ckanResource == null)
|
|
|
|
ckanResource = uploadFileOnCatalogue(csvFile, uuidKB, catalogue, username, resourceToAttachOnCkanName, resourceToAttachOnCkanDescription, apiKeyUser);
|
2016-11-27 12:33:41 +01:00
|
|
|
|
2017-07-13 15:43:19 +02:00
|
|
|
//upload this file on the folder of the vre (under .catalogue) and change the url of the resource
|
|
|
|
if(ckanResource != null){
|
2016-11-27 12:33:41 +01:00
|
|
|
|
2017-07-13 15:43:19 +02:00
|
|
|
if(createdFileOnWorkspace == null)
|
|
|
|
createdFileOnWorkspace = HelperMethods.uploadExternalFile(csvFolder, uuidKB + "_" + customAnnotation.key() + CSVHelpers.CSV_EXTENSION, resourceToAttachOnCkanDescription, csvFile);
|
2016-12-16 21:31:52 +01:00
|
|
|
|
2017-07-13 15:43:19 +02:00
|
|
|
if(createdFileOnWorkspace != null){
|
|
|
|
String publicUrlToSetOnCkan = createdFileOnWorkspace.getPublicLink(true);
|
2017-07-13 17:22:32 +02:00
|
|
|
|
|
|
|
// wait for patching..
|
|
|
|
Thread.sleep(500);
|
|
|
|
logger.debug("Going to patch the created resource with id " + ckanResource.getId() + " with url " + publicUrlToSetOnCkan);
|
2017-07-13 15:43:19 +02:00
|
|
|
boolean updated = catalogue.patchResource(ckanResource.getId(), publicUrlToSetOnCkan, resourceToAttachOnCkanName, resourceToAttachOnCkanDescription, "", apiKeyUser);
|
2016-12-16 21:31:52 +01:00
|
|
|
|
2017-07-13 15:43:19 +02:00
|
|
|
if(updated){
|
|
|
|
logger.info("Resource has been updated with the new url");
|
|
|
|
break;
|
|
|
|
}else
|
2017-07-13 17:22:32 +02:00
|
|
|
logger.error("Error while patching resource...");
|
2016-12-01 23:21:37 +01:00
|
|
|
}
|
2016-11-27 12:33:41 +01:00
|
|
|
}
|
2017-07-13 15:43:19 +02:00
|
|
|
|
2016-11-27 12:33:41 +01:00
|
|
|
}
|
2016-12-01 23:21:37 +01:00
|
|
|
|
2017-07-13 15:43:19 +02:00
|
|
|
// delete the file
|
|
|
|
csvFile.delete();
|
2016-11-27 12:33:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-07-13 15:43:19 +02:00
|
|
|
}catch(Exception e){
|
|
|
|
logger.warn("Failed to perform all the operations about this timeseries ", e);
|
2016-11-27 12:33:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while((current = current.getSuperclass())!=null); // iterate from the inherited class up to the Object.class
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-20 18:44:26 +01:00
|
|
|
/**
|
|
|
|
* Replace chars
|
|
|
|
* @param productName
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
private static String replaceIllegalChars(String productName) {
|
|
|
|
return productName.replaceAll("[/\\[\\],|:*.+]", "_");
|
|
|
|
}
|
|
|
|
|
2016-11-27 12:33:41 +01:00
|
|
|
/**
|
|
|
|
* Upload a resource on ckan
|
|
|
|
* @param csvFile
|
|
|
|
* @param packageName
|
|
|
|
* @param catalogue
|
|
|
|
* @param username
|
|
|
|
* @param resourceToAttachName
|
|
|
|
* @param description
|
|
|
|
* @return a ckan resource on success, null otherwise
|
|
|
|
*/
|
2016-11-28 12:20:43 +01:00
|
|
|
private static CkanResourceBase uploadFileOnCatalogue(File csvFile,
|
2017-02-23 18:55:32 +01:00
|
|
|
String uuidKB, DataCatalogue catalogue, String username,
|
2016-11-28 12:20:43 +01:00
|
|
|
String resourceToAttachName, String description, String apiKey) {
|
2016-11-27 12:33:41 +01:00
|
|
|
return catalogue.uploadResourceFile(
|
|
|
|
csvFile,
|
2017-02-23 18:55:32 +01:00
|
|
|
uuidKB,
|
2016-11-28 12:20:43 +01:00
|
|
|
apiKey,
|
2016-11-27 12:33:41 +01:00
|
|
|
resourceToAttachName,
|
|
|
|
description);
|
|
|
|
}
|
2017-07-12 18:05:35 +02:00
|
|
|
}
|