Merged changes made in branch

git-svn-id: https://svn.d4science.research-infrastructures.eu/gcube/trunk/data-catalogue/grsf-publisher-ws@171271 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
Luca Frosini 2018-09-03 09:07:06 +00:00
parent 163710176a
commit 9774bcd2ef
7 changed files with 316 additions and 246 deletions

View File

@ -9,6 +9,10 @@
<Change>Added 'Connected'-'Not Connected' tag to GRSF Records #11766</Change> <Change>Added 'Connected'-'Not Connected' tag to GRSF Records #11766</Change>
<Change>Added group for SDG flag #11767</Change> <Change>Added group for SDG flag #11767</Change>
<Change>Added citation field #11811</Change> <Change>Added citation field #11811</Change>
<Change>Added sub-groups support for available time series related to GRSF Type "Assessment Unit" #11832</Change>
<Change>Added Biomass group #11967</Change>
<Change>Changed 'State and trend of Marine Resource' to 'State and Trend' #11968</Change>
<Change>Changed 'Scientific advice' to 'Scientific Advice' #11969</Change>
</Changeset> </Changeset>
<Changeset component="org.gcube.data-catalogue.grsf-publisher-ws.1-5-0" <Changeset component="org.gcube.data-catalogue.grsf-publisher-ws.1-5-0"
date="2017-01-10"> date="2017-01-10">

View File

@ -11,7 +11,7 @@
<groupId>org.gcube.data-catalogue</groupId> <groupId>org.gcube.data-catalogue</groupId>
<artifactId>grsf-publisher-ws</artifactId> <artifactId>grsf-publisher-ws</artifactId>
<version>1.6.0-SNAPSHOT</version> <version>1.7.0-SNAPSHOT</version>
<packaging>war</packaging> <packaging>war</packaging>
<name>grsf-publisher-ws</name> <name>grsf-publisher-ws</name>

View File

@ -7,7 +7,8 @@ import java.lang.annotation.Target;
/** /**
* Group annotation * Group annotation
* @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) * @author Costantino Perciante (ISTI - CNR)
* @author Luca Frosini (ISTI - CNR)
*/ */
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD) @Target(ElementType.FIELD)
@ -26,4 +27,11 @@ public @interface Group {
*/ */
String groupNameOverValue() default ""; String groupNameOverValue() default "";
/**
* When the group is created the source is prepended to the group name.
* Set to false to avoid source prepending
* @return
*/
boolean prependSourceToGroupName() default true;
} }

View File

@ -26,6 +26,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
* Information that both Stock and Fishery records must contain. * Information that both Stock and Fishery records must contain.
* @author Costantino Perciante (ISTI - CNR) * @author Costantino Perciante (ISTI - CNR)
* @author Luca Frosini (ISTI - CNR) * @author Luca Frosini (ISTI - CNR)
*
*/ */
public abstract class Common extends Base{ public abstract class Common extends Base{
@ -72,12 +73,14 @@ public abstract class Common extends Base{
@JsonProperty(Constants.CATCHES_JSON_KEY) @JsonProperty(Constants.CATCHES_JSON_KEY)
@CustomField(key=Constants.CATCHES_CUSTOM_KEY) @CustomField(key=Constants.CATCHES_CUSTOM_KEY)
@TimeSeries @TimeSeries
@Group(groupNameOverValue=Constants.CATCHES_CUSTOM_KEY, prependSourceToGroupName=false)
@Valid @Valid
private List<TimeSeriesBean<String, String>> catches; private List<TimeSeriesBean<String, String>> catches;
@JsonProperty(Constants.LANDINGS_JSON_KEY) @JsonProperty(Constants.LANDINGS_JSON_KEY)
@CustomField(key=Constants.LANDINGS_CUSTOM_KEY) @CustomField(key=Constants.LANDINGS_CUSTOM_KEY)
@TimeSeries @TimeSeries
@Group(groupNameOverValue=Constants.LANDINGS_CUSTOM_KEY, prependSourceToGroupName=false)
@Valid @Valid
private List<TimeSeriesBean<String, String>> landings; private List<TimeSeriesBean<String, String>> landings;

View File

@ -16,9 +16,11 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* A fishery record bean. * A fishery record bean.
* @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) * @author Costantino Perciante (ISTI - CNR)
* @author Luca Frosini (ISTI - CNR)
*/ */
public class FisheryRecord extends Common{ public class FisheryRecord extends Common {
@JsonProperty(Constants.FISHERY_NAME_JSON_KEY) @JsonProperty(Constants.FISHERY_NAME_JSON_KEY)
@NotNull(message="fishery_name cannot be null") @NotNull(message="fishery_name cannot be null")

View File

@ -20,7 +20,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/** /**
* A stock record bean. * A stock record bean.
* @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) * @author Costantino Perciante (ISTI - CNR)
* @author Luca Frosini (ISTI - CNR)
*/ */
public class StockRecord extends Common{ public class StockRecord extends Common{
@ -50,47 +51,55 @@ public class StockRecord extends Common{
@JsonProperty(Constants.FIRMS_ABUNDANCE_LEVEL_JSON_KEY) @JsonProperty(Constants.FIRMS_ABUNDANCE_LEVEL_JSON_KEY)
@CustomField(key=Constants.FIRMS_ABUNDANCE_LEVEL_CUSTOM_KEY) @CustomField(key=Constants.FIRMS_ABUNDANCE_LEVEL_CUSTOM_KEY)
@TimeSeries @TimeSeries
@Group(groupNameOverValue=Constants.FIRMS_ABUNDANCE_LEVEL_CUSTOM_KEY, prependSourceToGroupName=false)
@Valid @Valid
private List<TimeSeriesBean<Abundance_Level, Void>> abundanceLevelStandard; private List<TimeSeriesBean<Abundance_Level, Void>> abundanceLevelStandard;
@JsonProperty(Constants.ABUNDANCE_LEVEL_JSON_KEY) @JsonProperty(Constants.ABUNDANCE_LEVEL_JSON_KEY)
@CustomField(key=Constants.ABUNDANCE_LEVEL_CUSTOM_KEY) @CustomField(key=Constants.ABUNDANCE_LEVEL_CUSTOM_KEY)
@TimeSeries @TimeSeries
@Group(groupNameOverValue=Constants.ABUNDANCE_LEVEL_CUSTOM_KEY, prependSourceToGroupName=false)
@Valid @Valid
private List<TimeSeriesBean<String, String>> abundanceLevel; private List<TimeSeriesBean<String, String>> abundanceLevel;
@JsonProperty(Constants.BIOMASS_JSON_KEY) @JsonProperty(Constants.BIOMASS_JSON_KEY)
@CustomField(key=Constants.BIOMASS_CUSTOM_KEY) @CustomField(key=Constants.BIOMASS_CUSTOM_KEY)
@TimeSeries @TimeSeries
@Group(groupNameOverValue=Constants.BIOMASS_CUSTOM_KEY, prependSourceToGroupName=false)
@Valid @Valid
private List<TimeSeriesBean<String, String>> biomass; private List<TimeSeriesBean<String, String>> biomass;
@JsonProperty(Constants.FISHING_PRESSURE_FIRMS_JSON_KEY) @JsonProperty(Constants.FISHING_PRESSURE_FIRMS_JSON_KEY)
@CustomField(key=Constants.FISHING_PRESSURE_FIRMS_CUSTOM_KEY) @CustomField(key=Constants.FISHING_PRESSURE_FIRMS_CUSTOM_KEY)
@TimeSeries @TimeSeries
@Group(groupNameOverValue=Constants.FISHING_PRESSURE_FIRMS_CUSTOM_KEY, prependSourceToGroupName=false)
@Valid @Valid
private List<TimeSeriesBean<Fishing_Pressure, Void>> fishingPressureStandard; private List<TimeSeriesBean<Fishing_Pressure, Void>> fishingPressureStandard;
@JsonProperty(Constants.FISHING_PRESSURE_JSON_KEY) @JsonProperty(Constants.FISHING_PRESSURE_JSON_KEY)
@CustomField(key=Constants.FISHING_PRESSURE_CUSTOM_KEY) @CustomField(key=Constants.FISHING_PRESSURE_CUSTOM_KEY)
@TimeSeries @TimeSeries
@Group(groupNameOverValue=Constants.FISHING_PRESSURE_CUSTOM_KEY, prependSourceToGroupName=false)
@Valid @Valid
private List<TimeSeriesBean<String, String>> fishingPressure; private List<TimeSeriesBean<String, String>> fishingPressure;
@JsonProperty(Constants.STATE_AND_TREND_MARINE_RESOURCE_JSON_KEY) @JsonProperty(Constants.STATE_AND_TREND_MARINE_RESOURCE_JSON_KEY)
@CustomField(key=Constants.STATE_AND_TREND_MARINE_RESOURCE_CUSTOM_KEY) @CustomField(key=Constants.STATE_AND_TREND_MARINE_RESOURCE_CUSTOM_KEY)
@TimeSeries @TimeSeries
@Group(groupNameOverValue=Constants.STATE_AND_TREND_MARINE_RESOURCE_CUSTOM_KEY, prependSourceToGroupName=false)
@Valid @Valid
private List<TimeSeriesBean<String, Void>> narrativeState; private List<TimeSeriesBean<String, Void>> narrativeState;
@JsonProperty(Constants.FAO_CATEGORIES_JSON_KEY) @JsonProperty(Constants.FAO_CATEGORIES_JSON_KEY)
@CustomField(key=Constants.FAO_CATEGORIES_CUSTOM_KEY) @CustomField(key=Constants.FAO_CATEGORIES_CUSTOM_KEY)
@TimeSeries @TimeSeries
@Group(groupNameOverValue=Constants.FAO_CATEGORIES_CUSTOM_KEY, prependSourceToGroupName=false)
@Valid @Valid
private List<TimeSeriesBean<String, Void>> faoState; private List<TimeSeriesBean<String, Void>> faoState;
@JsonProperty(Constants.SCIENTIFIC_ADVICE_JSON_KEY) @JsonProperty(Constants.SCIENTIFIC_ADVICE_JSON_KEY)
@CustomField(key=Constants.SCIENTIFIC_ADVICE_CUSTOM_KEY) @CustomField(key=Constants.SCIENTIFIC_ADVICE_CUSTOM_KEY)
@Group(groupNameOverValue=Constants.SCIENTIFIC_ADVICE_CUSTOM_KEY, prependSourceToGroupName=false)
private List<String> scientificAdvice; private List<String> scientificAdvice;
@JsonProperty(Constants.ASSESSOR_JSON_KEY) @JsonProperty(Constants.ASSESSOR_JSON_KEY)

View File

@ -36,6 +36,7 @@ import org.gcube.datacatalogue.common.Constants;
import org.gcube.datacatalogue.common.enums.Product_Type; import org.gcube.datacatalogue.common.enums.Product_Type;
import org.gcube.datacatalogue.common.enums.Sources; import org.gcube.datacatalogue.common.enums.Sources;
import org.gcube.datacatalogue.common.enums.Status; import org.gcube.datacatalogue.common.enums.Status;
import org.gcube.datacatalogue.common.enums.Stock_Type;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -44,25 +45,26 @@ import eu.trentorise.opendata.jackan.model.CkanLicense;
/** /**
* Services common utils. * Services common utils.
* @author Costantino Perciante at ISTI-CNR * @author Costantino Perciante (ISTI - CNR)
* @author Luca Frosini (ISTI - CNR)
*/ */
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
public class CommonServiceUtils { public class CommonServiceUtils {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CommonServiceUtils.class); private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CommonServiceUtils.class);
private static final int TAG_MAX_SIZE = 100; private static final int TAG_MAX_SIZE = 100;
private static Map<String, Boolean> extensionsCheck = new ConcurrentHashMap<>(); private static Map<String,Boolean> extensionsCheck = new ConcurrentHashMap<>();
/** /**
* Retrieve the list of licenses for stocks and fisheries * Retrieve the list of licenses for stocks and fisheries
* @return * @return
*/ */
public static Map<String, String> getLicenses(DataCatalogue catalogue){ public static Map<String,String> getLicenses(DataCatalogue catalogue) {
logger.info("Requested licenses..."); logger.info("Requested licenses...");
Map<String, String> toReturn = new HashMap<String, String>(); Map<String,String> toReturn = new HashMap<String,String>();
List<CkanLicense> licenses = catalogue.getLicenses(); List<CkanLicense> licenses = catalogue.getLicenses();
for (CkanLicense ckanLicense : licenses) { for(CkanLicense ckanLicense : licenses) {
toReturn.put(ckanLicense.getId(), ckanLicense.getTitle()); toReturn.put(ckanLicense.getId(), ckanLicense.getTitle());
} }
return toReturn; return toReturn;
@ -92,7 +94,7 @@ public class CommonServiceUtils {
throw new Exception("status cannot be null/empty"); throw new Exception("status cannot be null/empty");
// check if it is a stock and perform related checks // check if it is a stock and perform related checks
if(record.getClass().equals(StockRecord.class)){ if(record.getClass().equals(StockRecord.class)) {
StockRecord stock = (StockRecord) record; StockRecord stock = (StockRecord) record;
List<String> species = stock.getSpecies(); List<String> species = stock.getSpecies();
@ -101,14 +103,15 @@ public class CommonServiceUtils {
} }
// check if it is a stock and perform related checks // check if it is a stock and perform related checks
if(record.getClass().equals(FisheryRecord.class)){ if(record.getClass().equals(FisheryRecord.class)) {
FisheryRecord fishery = (FisheryRecord) record; FisheryRecord fishery = (FisheryRecord) record;
List<String> fishingArea = fishery.getFishingArea(); List<String> fishingArea = fishery.getFishingArea();
List<String> jurisdictionArea = fishery.getJurisdictionArea(); List<String> jurisdictionArea = fishery.getJurisdictionArea();
if((fishingArea == null || fishingArea.isEmpty()) && (jurisdictionArea == null || jurisdictionArea.isEmpty())) if((fishingArea == null || fishingArea.isEmpty())
&& (jurisdictionArea == null || jurisdictionArea.isEmpty()))
throw new Exception("fishing_area and jurisdiction_area cannot be null/empty at the same time!"); throw new Exception("fishing_area and jurisdiction_area cannot be null/empty at the same time!");
} }
} }
@ -126,23 +129,15 @@ public class CommonServiceUtils {
* @param username * @param username
* @param source * @param source
*/ */
public static void getTagsGroupsResourcesExtrasByRecord( public static void getTagsGroupsResourcesExtrasByRecord(Set<String> tags, boolean skipTags, Set<String> groups,
Set<String> tags, boolean skipGroups, List<ResourceBean> resources, boolean skipResources, Map<String,List<String>> extras,
boolean skipTags, Base record, String username, Sources source // it comes from the source type e.g., "grsf-", "ram-" ..
Set<String> groups, ) {
boolean skipGroups,
List<ResourceBean> resources,
boolean skipResources,
Map<String, List<String>> extras,
Base record,
String username,
Sources source // it comes from the source type e.g., "grsf-", "ram-" ..
){
Class<?> current = record.getClass(); Class<?> current = record.getClass();
do{ do {
Field[] fields = current.getDeclaredFields(); Field[] fields = current.getDeclaredFields();
for (Field field : fields) { for(Field field : fields) {
if(!skipTags) if(!skipTags)
getTagsByField(field, current, record, tags); getTagsByField(field, current, record, tags);
@ -156,8 +151,7 @@ public class CommonServiceUtils {
getResourcesByField(field, current, record, username, resources); getResourcesByField(field, current, record, username, resources);
} }
} } while((current = current.getSuperclass()) != null); // start from the inherited class up to the Object.class
while((current = current.getSuperclass())!=null); // start from the inherited class up to the Object.class
logger.debug("Tags are " + tags); logger.debug("Tags are " + tags);
logger.debug("Groups are " + groups); logger.debug("Groups are " + groups);
@ -168,37 +162,40 @@ public class CommonServiceUtils {
/** /**
* Retrieve the list of tags for this object * Retrieve the list of tags for this object
*/ */
private static void getTagsByField(Field field, Class<?> current, Base record, Set<String> tags){ private static void getTagsByField(Field field, Class<?> current, Base record, Set<String> tags) {
if(field.isAnnotationPresent(Tag.class)){ if(field.isAnnotationPresent(Tag.class)) {
try{ try {
Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record); Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record);
if(f != null){ if(f != null) {
if(f instanceof List<?>){ if(f instanceof List<?>) {
List asList = ((List) f); List asList = ((List) f);
if(!asList.isEmpty()){ if(!asList.isEmpty()) {
logger.debug("The object annotated with @Tag is a list. Adding ... "); logger.debug("The object annotated with @Tag is a list. Adding ... ");
int elementsToConsider = asList.size(); int elementsToConsider = asList.size();
// check if it is a time series, in this take the last X elements // check if it is a time series, in this take the last X elements
if(asList.get(0).getClass().equals(TimeSeriesBean.class)){ if(asList.get(0).getClass().equals(TimeSeriesBean.class)) {
elementsToConsider = Math.min(elementsToConsider, Constants.TIME_SERIES_TAKE_LAST_VALUES); elementsToConsider = Math.min(elementsToConsider,
for (int i = 0; i < elementsToConsider; i++) { Constants.TIME_SERIES_TAKE_LAST_VALUES);
String finalTag = asList.get(i).toString().trim().replaceAll(Constants.REGEX_TAGS, ""); for(int i = 0; i < elementsToConsider; i++) {
String finalTag = asList.get(i).toString().trim().replaceAll(Constants.REGEX_TAGS,
"");
if(finalTag.length() <= TAG_MAX_SIZE) if(finalTag.length() <= TAG_MAX_SIZE)
tags.add(finalTag); tags.add(finalTag);
} }
}else{ } else {
// else add all the available elements // else add all the available elements
for (int i = 0; i < elementsToConsider; i++) { for(int i = 0; i < elementsToConsider; i++) {
String finalTag = asList.get(i).toString().trim().replaceAll(Constants.REGEX_TAGS, ""); String finalTag = asList.get(i).toString().trim().replaceAll(Constants.REGEX_TAGS,
"");
if(finalTag.length() <= TAG_MAX_SIZE) if(finalTag.length() <= TAG_MAX_SIZE)
tags.add(finalTag); tags.add(finalTag);
} }
} }
} }
}else{ } else {
logger.debug("The object annotated with @Tag is a simple one. Adding ... "); logger.debug("The object annotated with @Tag is a simple one. Adding ... ");
String finalTag = f.toString().trim().replaceAll(Constants.REGEX_TAGS, ""); String finalTag = f.toString().trim().replaceAll(Constants.REGEX_TAGS, "");
logger.debug(finalTag); logger.debug(finalTag);
@ -207,7 +204,7 @@ public class CommonServiceUtils {
} }
} }
}catch(Exception e){ } catch(Exception e) {
logger.error("Failed to read value for field " + field.getName() + " skipping", e); logger.error("Failed to read value for field " + field.getName() + " skipping", e);
} }
} }
@ -216,47 +213,82 @@ public class CommonServiceUtils {
/** /**
* Retrieve the list of groups' names for this object * Retrieve the list of groups' names for this object
*/ */
private static void getGroupsByField(Field field, Class<?> current, Base record, Set<String> groups, Sources source){ private static void getGroupsByField(Field field, Class<?> current, Base record, Set<String> groups,
if(field.isAnnotationPresent(Group.class)){ Sources source) {
String conditionToCheck = field.getAnnotation(Group.class).condition(); if(field.isAnnotationPresent(Group.class)) {
String groupNameOverValue = field.getAnnotation(Group.class).groupNameOverValue(); Group group = field.getAnnotation(Group.class);
try{ String conditionToCheck = group.condition();
String groupNameOverValue = group.groupNameOverValue();
// See https://support.d4science.org/issues/11832
boolean assessmentUnit = false;
boolean prependSource = group.prependSourceToGroupName();
if(record instanceof StockRecord) {
StockRecord stockRecord = (StockRecord) record;
Stock_Type stock_Type = stockRecord.getType();
if(stock_Type != Stock_Type.Assessment_Unit) {
prependSource = false;
}else {
assessmentUnit = true;
}
}
// end patch for https://support.d4science.org/issues/11832
try {
Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record); Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record);
if(f != null){ if(f != null) {
if(f instanceof List<?>){ if(f instanceof List<?>) {
List asList = ((List) f); List asList = ((List) f);
if(!asList.isEmpty()){ if(!asList.isEmpty()) {
logger.debug("The object annotated with @Group is a list. Adding ... "); logger.debug("The object annotated with @Group is a list. Adding ... ");
// else add all the available elements // else add all the available elements
for (int i = 0; i < asList.size(); i++) { for(int i = 0; i < asList.size(); i++) {
boolean match = conditionToCheck.isEmpty() ? true : asList.get(i).toString().trim().matches(conditionToCheck); boolean match = conditionToCheck.isEmpty() ? true
if(match){ : asList.get(i).toString().trim().matches(conditionToCheck);
String groupName = groupNameOverValue.isEmpty() ? if(match) {
HelperMethods.getGroupNameOnCkan(source.toString().toLowerCase() + "-" + asList.get(i).toString().trim()) : String groupName = groupNameOverValue.isEmpty()
source.toString().toLowerCase() + "-" + groupNameOverValue; ? HelperMethods.getGroupNameOnCkan(source.toString().toLowerCase() + "-"
+ asList.get(i).toString().trim())
: source.toString().toLowerCase() + "-" + groupNameOverValue;
if(assessmentUnit && !prependSource) {
groups.add(groupNameOverValue);
}else {
groups.add(groupName); groups.add(groupName);
} }
} }
}
} }
}else{
} else {
// also convert to the group name that should be on ckan // also convert to the group name that should be on ckan
boolean match = conditionToCheck.isEmpty() ? true : f.toString().trim().matches(conditionToCheck); boolean match = conditionToCheck.isEmpty() ? true
if(match){ : f.toString().trim().matches(conditionToCheck);
if(match) {
String groupName = groupNameOverValue.isEmpty() ? String groupName = groupNameOverValue.isEmpty()
HelperMethods.getGroupNameOnCkan(source.toString().toLowerCase() + "-" + f.toString().trim()) : ? HelperMethods.getGroupNameOnCkan(
source.toString().toLowerCase() + "-" + groupNameOverValue; source.toString().toLowerCase() + "-" + f.toString().trim())
: source.toString().toLowerCase() + "-" + groupNameOverValue;
if(assessmentUnit && !prependSource) {
groups.add(groupNameOverValue);
}else {
groups.add(groupName); groups.add(groupName);
}
} }
} }
} }
}catch(Exception e){ } catch(Exception e) {
logger.error("Failed to read value for field " + field.getName() + " skipping", e); logger.error("Failed to read value for field " + field.getName() + " skipping", e);
} }
} }
@ -267,31 +299,32 @@ public class CommonServiceUtils {
* Retrieve the list of extras for this object * Retrieve the list of extras for this object
* @param source * @param source
*/ */
private static void getExtrasByField(Field field, Class<?> current, Base record, Map<String, List<String>> extras, Sources source){ private static void getExtrasByField(Field field, Class<?> current, Base record, Map<String,List<String>> extras,
if(field.isAnnotationPresent(CustomField.class)){ Sources source) {
try{ if(field.isAnnotationPresent(CustomField.class)) {
try {
Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record); Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record);
String keyField = field.getAnnotation(CustomField.class).key(); String keyField = field.getAnnotation(CustomField.class).key();
// manage no connections nor similar grsf records here for GRSF records only // manage no connections nor similar grsf records here for GRSF records only
if(source.equals(Sources.GRSF) && keyField.equals(Constants.SIMILAR_GRSF_RECORDS_CUSTOM_KEY)){ if(source.equals(Sources.GRSF) && keyField.equals(Constants.SIMILAR_GRSF_RECORDS_CUSTOM_KEY)) {
List asList = (List)f; List asList = (List) f;
if(asList == null || asList.isEmpty()){ if(asList == null || asList.isEmpty()) {
extras.put(keyField, Arrays.asList(Constants.NO_SIMILAR_GRSF_RECORDS)); extras.put(keyField, Arrays.asList(Constants.NO_SIMILAR_GRSF_RECORDS));
return; return;
} }
} }
if(source.equals(Sources.GRSF) && keyField.equals(Constants.CONNECTED_CUSTOM_KEY)){ if(source.equals(Sources.GRSF) && keyField.equals(Constants.CONNECTED_CUSTOM_KEY)) {
List asList = (List)f; List asList = (List) f;
if(asList == null || asList.isEmpty()){ if(asList == null || asList.isEmpty()) {
extras.put(keyField, Arrays.asList(Constants.NO_CONNECTED_RECORDS)); extras.put(keyField, Arrays.asList(Constants.NO_CONNECTED_RECORDS));
return; return;
} }
} }
if(f != null){ if(f != null) {
Set<String> valuesForKey = null; Set<String> valuesForKey = null;
// check if the map already contains this key // check if the map already contains this key
@ -300,30 +333,31 @@ public class CommonServiceUtils {
else else
valuesForKey = new HashSet<String>(); valuesForKey = new HashSet<String>();
if(f instanceof List<?>){ if(f instanceof List<?>) {
logger.debug("The object " + field.getName() + " is a list and is annotated with @CustomField. Adding ..."); logger.debug("The object " + field.getName()
List asList = (List)f; + " is a list and is annotated with @CustomField. Adding ...");
if(!asList.isEmpty()){ List asList = (List) f;
if(!asList.isEmpty()) {
int elementsToConsider = asList.size(); int elementsToConsider = asList.size();
// check if it is a time series, in this case take the last X elements // check if it is a time series, in this case take the last X elements
if(asList.get(0).getClass().equals(TimeSeriesBean.class)){ if(asList.get(0).getClass().equals(TimeSeriesBean.class)) {
elementsToConsider = Math.min(elementsToConsider, Constants.TIME_SERIES_TAKE_LAST_VALUES); elementsToConsider = Math.min(elementsToConsider,
for (int i = 0; i < elementsToConsider; i++) { Constants.TIME_SERIES_TAKE_LAST_VALUES);
for(int i = 0; i < elementsToConsider; i++) {
// trim and remove html // trim and remove html
String clean = HelperMethods.removeHTML(asList.get(i).toString().trim()); String clean = HelperMethods.removeHTML(asList.get(i).toString().trim());
valuesForKey.add(clean); valuesForKey.add(clean);
} }
} } else
else for(int i = 0; i < elementsToConsider; i++) {
for (int i = 0; i < elementsToConsider; i++) {
String clean = HelperMethods.removeHTML(asList.get(i).toString().trim()); String clean = HelperMethods.removeHTML(asList.get(i).toString().trim());
valuesForKey.add(clean); valuesForKey.add(clean);
} }
} }
}else{ } else {
String clean = HelperMethods.removeHTML(f.toString().trim()); String clean = HelperMethods.removeHTML(f.toString().trim());
valuesForKey.add(clean); valuesForKey.add(clean);
} }
@ -331,7 +365,7 @@ public class CommonServiceUtils {
// add to the map // add to the map
extras.put(keyField, new ArrayList<String>(valuesForKey)); extras.put(keyField, new ArrayList<String>(valuesForKey));
} }
}catch(Exception e){ } catch(Exception e) {
logger.error("Failed to read value for field " + field.getName() + " skipping", e); logger.error("Failed to read value for field " + field.getName() + " skipping", e);
} }
} }
@ -345,28 +379,31 @@ public class CommonServiceUtils {
* @param resources * @param resources
* @return * @return
*/ */
private static void getResourcesByField(Field field, Class<?> current, Base record, String username, List<ResourceBean> resources){ private static void getResourcesByField(Field field, Class<?> current, Base record, String username,
if(field.isAnnotationPresent(CkanResource.class)){ List<ResourceBean> resources) {
try{ if(field.isAnnotationPresent(CkanResource.class)) {
try {
Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record); Object f = new PropertyDescriptor(field.getName(), current).getReadMethod().invoke(record);
if(f != null){ if(f != null) {
if(f instanceof List<?>){ if(f instanceof List<?>) {
List<Resource> listOfResources = (List<Resource>)f; List<Resource> listOfResources = (List<Resource>) f;
for (Resource resource : listOfResources) { for(Resource resource : listOfResources) {
resources.add(new ResourceBean(resource.getUrl(), resource.getName().toString(), resource.getDescription(), null, username, null, null)); resources.add(new ResourceBean(resource.getUrl(), resource.getName().toString(),
resource.getDescription(), null, username, null, null));
} }
}else{ } else {
Resource res = (Resource)f; Resource res = (Resource) f;
resources.add(new ResourceBean(res.getUrl(), res.getName().toString(), res.getDescription(), null, username, null, null)); resources.add(new ResourceBean(res.getUrl(), res.getName().toString(), res.getDescription(),
null, username, null, null));
} }
} }
}catch(Exception e){ } catch(Exception e) {
logger.error("Failed to read value for field " + field.getName() + " skipping", e); logger.error("Failed to read value for field " + field.getName() + " skipping", e);
} }
} }
@ -376,13 +413,15 @@ public class CommonServiceUtils {
* Evaluate if the user has the admin role * Evaluate if the user has the admin role
* Throws exception if he/she doesn't * Throws exception if he/she doesn't
*/ */
public static void hasAdminRole(String username, DataCatalogue catalogue, String apiKey, String organization) throws Exception{ public static void hasAdminRole(String username, DataCatalogue catalogue, String apiKey, String organization)
throws Exception {
String role = catalogue.getRoleOfUserInOrganization(username, organization, apiKey); String role = catalogue.getRoleOfUserInOrganization(username, organization, apiKey);
logger.info("Role of the user " + username + " is " + role + " in " + organization); logger.info("Role of the user " + username + " is " + role + " in " + organization);
if(role == null || role.isEmpty() || !role.equalsIgnoreCase(RolesCkanGroupOrOrg.ADMIN.toString())) if(role == null || role.isEmpty() || !role.equalsIgnoreCase(RolesCkanGroupOrOrg.ADMIN.toString()))
throw new Exception("You are not authorized to create a product. Please check you have the Catalogue-Administrator role!"); throw new Exception(
"You are not authorized to create a product. Please check you have the Catalogue-Administrator role!");
} }
@ -394,14 +433,15 @@ public class CommonServiceUtils {
*/ */
public static void checkName(String futureName, DataCatalogue catalogue) throws Exception { public static void checkName(String futureName, DataCatalogue catalogue) throws Exception {
if(!HelperMethods.isNameValid(futureName)){ if(!HelperMethods.isNameValid(futureName)) {
throw new Exception("The 'uuid_knowledge_base' must contain only alphanumeric characters, and symbols like '.' or '_', '-'"); throw new Exception(
}else{ "The 'uuid_knowledge_base' must contain only alphanumeric characters, and symbols like '.' or '_', '-'");
} else {
logger.debug("Checking if such name [" + futureName + "] doesn't exist ..."); logger.debug("Checking if such name [" + futureName + "] doesn't exist ...");
boolean alreadyExists = catalogue.existProductWithNameOrId(futureName); boolean alreadyExists = catalogue.existProductWithNameOrId(futureName);
if(alreadyExists){ if(alreadyExists) {
logger.debug("A product with 'uuid_knowledge_base' " + futureName + " already exists"); logger.debug("A product with 'uuid_knowledge_base' " + futureName + " already exists");
throw new Exception("A product with 'uuid_knowledge_base' " + futureName + " already exists"); throw new Exception("A product with 'uuid_knowledge_base' " + futureName + " already exists");
@ -425,13 +465,14 @@ public class CommonServiceUtils {
* @throws Exception * @throws Exception
*/ */
public static void validateRecordAndMapFields(String apiKey, String context, ServletContext contextServlet, public static void validateRecordAndMapFields(String apiKey, String context, ServletContext contextServlet,
Sources sourceInPath, Common record, Product_Type productType, Set<String> tags, Map<String, List<String>> customFields, Sources sourceInPath, Common record, Product_Type productType, Set<String> tags,
Set<String> groups, List<ResourceBean> resources, String username, String futureTitle) throws Exception { Map<String,List<String>> customFields, Set<String> groups, List<ResourceBean> resources, String username,
String futureTitle) throws Exception {
// validate the record if it is a GRSF one and set the record type and in manage context // validate the record if it is a GRSF one and set the record type and in manage context
// Status field is needed only in the Manage context for GRSF records // Status field is needed only in the Manage context for GRSF records
if(context.equals((String)contextServlet.getInitParameter(HelperMethods.MANAGE_CONTEX_KEY))){ if(context.equals((String) contextServlet.getInitParameter(HelperMethods.MANAGE_CONTEX_KEY))) {
if(sourceInPath.equals(Sources.GRSF)){ if(sourceInPath.equals(Sources.GRSF)) {
List<RefersToBean> refersTo = record.getRefersTo(); List<RefersToBean> refersTo = record.getRefersTo();
if(refersTo == null || refersTo.isEmpty()) if(refersTo == null || refersTo.isEmpty())
@ -441,9 +482,10 @@ public class CommonServiceUtils {
String databaseSource = ""; String databaseSource = "";
// we have the id within the catalog of this record. This means that we can retrieve the record and its system:type // we have the id within the catalog of this record. This means that we can retrieve the record and its system:type
for (RefersToBean refersToBean : refersTo) { for(RefersToBean refersToBean : refersTo) {
String sourceOrganization = getRecordOrganization(refersToBean.getId(), apiKey, context); String sourceOrganization = getRecordOrganization(refersToBean.getId(), apiKey, context);
resources.add(new ResourceBean(refersToBean.getUrl(), sourceOrganization , "", null, username, null, null)); resources.add(new ResourceBean(refersToBean.getUrl(), sourceOrganization, "", null, username, null,
null));
sourcesList.add(sourceOrganization.toLowerCase()); sourcesList.add(sourceOrganization.toLowerCase());
databaseSource += sourceOrganization + " "; databaseSource += sourceOrganization + " ";
} }
@ -463,8 +505,10 @@ public class CommonServiceUtils {
record.setDomain(productType.getOrigName()); record.setDomain(productType.getOrigName());
// set system type (it is equal to the GRSF Type for GRSF records, "Legacy" for source records) // set system type (it is equal to the GRSF Type for GRSF records, "Legacy" for source records)
record.setSystemType(sourceInPath.equals(Sources.GRSF) ? record.setSystemType(
productType.equals(Product_Type.FISHERY) ? ((FisheryRecord)record).getType().getOrigName() : ((StockRecord)record).getType().getOrigName() sourceInPath.equals(Sources.GRSF)
? productType.equals(Product_Type.FISHERY) ? ((FisheryRecord) record).getType().getOrigName()
: ((StockRecord) record).getType().getOrigName()
: Constants.SYSTEM_TYPE_FOR_SOURCES_VALUE); : Constants.SYSTEM_TYPE_FOR_SOURCES_VALUE);
logger.debug("Domain is " + productType.getOrigName() + " and system type " + record.getSystemType()); logger.debug("Domain is " + productType.getOrigName() + " and system type " + record.getSystemType());
@ -472,7 +516,8 @@ public class CommonServiceUtils {
// evaluate the custom fields/tags, resources and groups // evaluate the custom fields/tags, resources and groups
groups.add(sourceInPath.getOrigName().toLowerCase() + "-" + productType.getOrigName().toLowerCase()); //e.g. grsf-fishery groups.add(sourceInPath.getOrigName().toLowerCase() + "-" + productType.getOrigName().toLowerCase()); //e.g. grsf-fishery
boolean skipTags = !sourceInPath.equals(Sources.GRSF); // no tags for the Original records boolean skipTags = !sourceInPath.equals(Sources.GRSF); // no tags for the Original records
CommonServiceUtils.getTagsGroupsResourcesExtrasByRecord(tags, skipTags, groups, false, resources, false, customFields, record, username, sourceInPath); CommonServiceUtils.getTagsGroupsResourcesExtrasByRecord(tags, skipTags, groups, false, resources, false,
customFields, record, username, sourceInPath);
} }
@ -483,12 +528,12 @@ public class CommonServiceUtils {
* @param productType * @param productType
* @param sourceInPath * @param sourceInPath
*/ */
private static void addRecordToGroupSources(Set<String> groups, private static void addRecordToGroupSources(Set<String> groups, List<String> sourcesList, Product_Type productType,
List<String> sourcesList, Product_Type productType, Sources sourceInPath) { Sources sourceInPath) {
Collections.sort(sourcesList); // be sure the name are sorted because the groups have been generated this way Collections.sort(sourcesList); // be sure the name are sorted because the groups have been generated this way
String groupName = sourceInPath.getOrigName().toLowerCase() + "-" + productType.getOrigName().toLowerCase(); String groupName = sourceInPath.getOrigName().toLowerCase() + "-" + productType.getOrigName().toLowerCase();
for (String source : sourcesList) { for(String source : sourcesList) {
groupName += "-" + source; groupName += "-" + source;
} }
@ -502,7 +547,7 @@ public class CommonServiceUtils {
* @return null on error * @return null on error
* @throws Exception * @throws Exception
*/ */
public static String getSystemTypeValue(String itemIdOrName, String apiKey, String context) throws Exception{ public static String getSystemTypeValue(String itemIdOrName, String apiKey, String context) throws Exception {
DataCatalogue catalog = HelperMethods.getDataCatalogueRunningInstance(context); DataCatalogue catalog = HelperMethods.getDataCatalogueRunningInstance(context);
CkanDataset dataset = catalog.getDataset(itemIdOrName, apiKey); CkanDataset dataset = catalog.getDataset(itemIdOrName, apiKey);
@ -516,7 +561,7 @@ public class CommonServiceUtils {
} }
public static String getRecordOrganization(String itemIdOrName, String apiKey, String context) throws Exception{ public static String getRecordOrganization(String itemIdOrName, String apiKey, String context) throws Exception {
DataCatalogue catalog = HelperMethods.getDataCatalogueRunningInstance(context); DataCatalogue catalog = HelperMethods.getDataCatalogueRunningInstance(context);
CkanDataset dataset = catalog.getDataset(itemIdOrName, apiKey); CkanDataset dataset = catalog.getDataset(itemIdOrName, apiKey);
if(dataset == null) if(dataset == null)
@ -539,18 +584,20 @@ public class CommonServiceUtils {
* @param partialDescription * @param partialDescription
* @throws InterruptedException * @throws InterruptedException
*/ */
public static void actionsPostCreateOrUpdate( public static void actionsPostCreateOrUpdate(final String datasetId, final String futureName, final Common record,
final String datasetId, final String futureName, final Common record, final String apiKey, final String username, final String organization, String itemUrl, final String apiKey, final String username, final String organization, String itemUrl,
ResponseCreationBean responseBean, final DataCatalogue catalogue, ResponseCreationBean responseBean, final DataCatalogue catalogue, Map<String,String> namespaces,
Map<String, String> namespaces, final Set<String> groups, final String context, final Set<String> groups, final String context, final String token, final String futureTitle,
final String token, final String futureTitle, final String authorFullname, final ServletContext contextServlet, final boolean isUpdated, final String authorFullname, final ServletContext contextServlet, final boolean isUpdated,
String description) throws InterruptedException { String description) throws InterruptedException {
// on create, we need to add the item url... the description can be set on create and update instead // on create, we need to add the item url... the description can be set on create and update instead
if(!isUpdated){ if(!isUpdated) {
itemUrl = catalogue.getUnencryptedUrlFromDatasetIdOrName(futureName); itemUrl = catalogue.getUnencryptedUrlFromDatasetIdOrName(futureName);
Map<String, List<String>> addField = new HashMap<String, List<String>>(); Map<String,List<String>> addField = new HashMap<String,List<String>>();
String modifiedUUIDKey = namespaces.containsKey(Constants.ITEM_URL_FIELD) ? namespaces.get(Constants.ITEM_URL_FIELD) : Constants.ITEM_URL_FIELD; String modifiedUUIDKey = namespaces.containsKey(Constants.ITEM_URL_FIELD)
? namespaces.get(Constants.ITEM_URL_FIELD)
: Constants.ITEM_URL_FIELD;
addField.put(modifiedUUIDKey, Arrays.asList(itemUrl)); addField.put(modifiedUUIDKey, Arrays.asList(itemUrl));
catalogue.patchProductCustomFields(datasetId, apiKey, addField, false); catalogue.patchProductCustomFields(datasetId, apiKey, addField, false);
} }
@ -575,9 +622,10 @@ public class CommonServiceUtils {
public void run() { public void run() {
try { try {
// manage groups (wait thread to die: ckan doesn't support too much concurrency on same record ...) // manage groups (wait thread to die: ckan doesn't support too much concurrency on same record ...)
if(!groups.isEmpty()){ if(!groups.isEmpty()) {
logger.info("Launching thread for association to the list of groups " + groups); logger.info("Launching thread for association to the list of groups " + groups);
AssociationToGroupThread threadGroups = new AssociationToGroupThread(new ArrayList<String>(groups), datasetId, organization, username, catalogue, apiKey); AssociationToGroupThread threadGroups = new AssociationToGroupThread(
new ArrayList<String>(groups), datasetId, organization, username, catalogue, apiKey);
threadGroups.start(); threadGroups.start();
threadGroups.join(); threadGroups.join();
} }
@ -586,18 +634,13 @@ public class CommonServiceUtils {
new ManageTimeSeriesThread(record, futureName, username, catalogue, context, token).start(); new ManageTimeSeriesThread(record, futureName, username, catalogue, context, token).start();
// write a post if the product has been published in grsf context // write a post if the product has been published in grsf context
if(!isUpdated && context.equals((String)contextServlet.getInitParameter(HelperMethods.PUBLIC_CONTEX_KEY))){ if(!isUpdated && context
new WritePostCatalogueManagerThread( .equals((String) contextServlet.getInitParameter(HelperMethods.PUBLIC_CONTEX_KEY))) {
context, new WritePostCatalogueManagerThread(context, token, futureTitle, itemUrlForThread, true,
token, new ArrayList<String>(), authorFullname).start();
futureTitle,
itemUrlForThread,
true,
new ArrayList<String>(),
authorFullname).start();
logger.info("Thread to write a post about the new product has been launched"); logger.info("Thread to write a post about the new product has been launched");
} }
}catch (InterruptedException e) { } catch(InterruptedException e) {
logger.error("Error", e); logger.error("Error", e);
} }
} }
@ -611,12 +654,13 @@ public class CommonServiceUtils {
* @param organization * @param organization
* @param admin * @param admin
*/ */
public static void extendRoleToOtherOrganizations(String username, DataCatalogue catalogue, String organization, RolesCkanGroupOrOrg admin) { public static void extendRoleToOtherOrganizations(String username, DataCatalogue catalogue, String organization,
RolesCkanGroupOrOrg admin) {
logger.debug("Checking if role extension is needed here"); logger.debug("Checking if role extension is needed here");
if(extensionsCheck.containsKey(username) && extensionsCheck.get(username)) if(extensionsCheck.containsKey(username) && extensionsCheck.get(username))
return; return;
else{ else {
catalogue.assignRolesOtherOrganization(username, organization, admin); catalogue.assignRolesOtherOrganization(username, organization, admin);
extensionsCheck.put(username, true); extensionsCheck.put(username, true);
} }