diff --git a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/client/GcubeCkanDataCatalogService.java b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/client/GcubeCkanDataCatalogService.java index 7bb0e5e..2d366fd 100644 --- a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/client/GcubeCkanDataCatalogService.java +++ b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/client/GcubeCkanDataCatalogService.java @@ -85,7 +85,7 @@ public interface GcubeCkanDataCatalogService extends RemoteService { /** * Notify product update */ - boolean notifyProductUpdate(ManageProductBean bean); + String notifyProductUpdate(ManageProductBean bean); /** * Get the product bean from the product identifier diff --git a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/client/GcubeCkanDataCatalogServiceAsync.java b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/client/GcubeCkanDataCatalogServiceAsync.java index f9c2a2d..2b62b4c 100644 --- a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/client/GcubeCkanDataCatalogServiceAsync.java +++ b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/client/GcubeCkanDataCatalogServiceAsync.java @@ -91,7 +91,7 @@ public interface GcubeCkanDataCatalogServiceAsync { * Notify product update */ void notifyProductUpdate(ManageProductBean bean, - AsyncCallback callback); + AsyncCallback callback); void getProductBeanById(String identifier, AsyncCallback callback); diff --git a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/client/ui/ManageProductWidget.java b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/client/ui/ManageProductWidget.java index 97f3e0f..6a2a85a 100644 --- a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/client/ui/ManageProductWidget.java +++ b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/client/ui/ManageProductWidget.java @@ -186,18 +186,18 @@ public class ManageProductWidget extends Composite{ bean.setAnnotation(new HTML(annotationArea.getText().trim()).getText()); bean.setNewStatus(GRSFStatus.fromString(listBoxStatus.getSelectedItemText())); - service.notifyProductUpdate(bean, new AsyncCallback() { + service.notifyProductUpdate(bean, new AsyncCallback() { @Override - public void onSuccess(Boolean result) { + public void onSuccess(String result) { - if(result){ + if(result == null){ showInfo(STATUS_UPDATE_SUCCESS, AlertType.SUCCESS); confirmButton.removeFromParent(); formUpdate.setVisible(false); }else{ - showInfo(STATUS_UPDATE_ERROR, AlertType.ERROR); + showInfo(STATUS_UPDATE_ERROR + "(" + result + ")", AlertType.ERROR); confirmButton.setEnabled(true); } diff --git a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/server/GcubeCkanDataCatalogServiceImpl.java b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/server/GcubeCkanDataCatalogServiceImpl.java index 8fa8421..02e7063 100644 --- a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/server/GcubeCkanDataCatalogServiceImpl.java +++ b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/server/GcubeCkanDataCatalogServiceImpl.java @@ -525,7 +525,7 @@ public class GcubeCkanDataCatalogServiceImpl extends RemoteServiceServlet implem } @Override - public boolean notifyProductUpdate(ManageProductBean bean) { + public String notifyProductUpdate(ManageProductBean bean) { logger.info("Creating notification for the bean " + bean + " to send to the knowledge base"); try{ @@ -540,15 +540,12 @@ public class GcubeCkanDataCatalogServiceImpl extends RemoteServiceServlet implem baseUrl = GRSFNotificationService.discoverEndPoint(context); getThreadLocalRequest().getSession().setAttribute(keyPerContext, baseUrl); } - String result = GRSFNotificationService.updateCatalogueRecord(baseUrl, bean, catalogue, SessionUtil.getCurrentUser(getThreadLocalRequest()).getUsername()); - - if(result == null) - return true; + return GRSFNotificationService.updateCatalogueRecord(baseUrl, bean, catalogue, SessionUtil.getCurrentUser(getThreadLocalRequest()).getUsername()); }catch(Exception e){ logger.error("Unable to update the product.." + e.getMessage()); + return e.getMessage(); } - return false; } @Override diff --git a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/server/manage/GRSFNotificationService.java b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/server/manage/GRSFNotificationService.java index 8479fa3..189838a 100644 --- a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/server/manage/GRSFNotificationService.java +++ b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/server/manage/GRSFNotificationService.java @@ -3,6 +3,9 @@ package org.gcube.portlets.gcubeckan.gcubeckandatacatalog.server.manage; import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; import java.util.Iterator; import java.util.List; @@ -13,6 +16,8 @@ import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue; import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.ManageProductBean; import org.gcube.resources.discovery.client.api.DiscoveryClient; import org.gcube.resources.discovery.client.queries.api.SimpleQuery; +import org.gcube.vomanagement.usermanagement.impl.LiferayUserManager; +import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.slf4j.Logger; @@ -20,10 +25,15 @@ 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.HttpPost; +import eu.trentorise.opendata.jackan.internal.org.apache.http.entity.ContentType; import eu.trentorise.opendata.jackan.internal.org.apache.http.entity.StringEntity; 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.internal.org.apache.http.util.EntityUtils; +import eu.trentorise.opendata.jackan.model.CkanDataset; +import eu.trentorise.opendata.jackan.model.CkanGroup; +import eu.trentorise.opendata.jackan.model.CkanPair; +import eu.trentorise.opendata.jackan.model.CkanTag; /** * Endpoint for sending update records information to GRSF KB @@ -33,6 +43,10 @@ public class GRSFNotificationService { private static Logger logger = LoggerFactory.getLogger(GRSFNotificationService.class); private static final String SERVICE_POST_METHOD = "/service/updater/post"; + private static final String ANNOTATION_KEY = "Annotation on update"; + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private static final String STATUS_CUSTOM_FIELD_KEY = "Status"; + private static final int MAX_TRIAL = 5; // request post fields private static final String CATALOGUE_ID = "catalog_id"; @@ -145,13 +159,121 @@ public class GRSFNotificationService { "and error in the result bean is " + parsedJSON.get(ERROR)); // patch the catalogue product - new PatchProductThread(catalogue, bean, username).start(); + return patchProduct(catalogue, bean, username); }catch(Exception e){ - logger.error("Unable to update this record", e); + logger.error("Unable to update this record" + e.getMessage()); return e.getMessage(); } - return null; + } + + /** + * Patch the product + * @param catalogue + * @param bean + * @param username + */ + @SuppressWarnings("unchecked") + private static String patchProduct(DataCatalogue catalogue, + ManageProductBean bean, String username) { + + logger.info("Going to patch record in the catalogue with identifier " + bean.getCatalogueIdentifier() + + " from user " + username); + + String apiKey = catalogue.getApiKeyFromUsername(username); + CkanDataset dataset = catalogue.getDataset(bean.getCatalogueIdentifier(), apiKey); + String errorMessage = null; + + for (int i = 0; i < MAX_TRIAL; i++) { + + try(CloseableHttpClient httpClient = HttpClientBuilder.create().build();){ + + JSONObject jsonRequest = new JSONObject(); + JSONArray tagsAsJson = new JSONArray(); + JSONArray groupsAsJson = new JSONArray(); + JSONArray customFieldsAsJson = new JSONArray(); + + // manage the custom fields + List extras = dataset.getExtras(); + for (CkanPair ckanPair : extras) { + if(ckanPair.getKey().equals(STATUS_CUSTOM_FIELD_KEY) && ckanPair.getValue().equals(bean.getCurrentStatus().toString())) + continue; + + JSONObject obj = new JSONObject(); + obj.put("key", ckanPair.getKey()); + obj.put("value", ckanPair.getValue()); + customFieldsAsJson.add(obj); + } + + // add the new one and the annotation message + JSONObject newStatus = new JSONObject(); + newStatus.put(STATUS_CUSTOM_FIELD_KEY, bean.getNewStatus().toString()); + customFieldsAsJson.add(newStatus); + + JSONObject newAnnotation = new JSONObject(); + newAnnotation.put(ANNOTATION_KEY, Arrays.asList("date: " + DATE_FORMAT.format(new Date()) + + ", admin: " + new LiferayUserManager().getUserByUsername(username).getFullname() + + ", message: " + (bean.getAnnotation() != null ? bean.getAnnotation().replaceAll("\"", "") : "none") + + ", old status: " + bean.getCurrentStatus().toString() + + ", new status: " + bean.getNewStatus().toString() + )); + customFieldsAsJson.add(newAnnotation); + + // manage the tags + List tags = dataset.getTags(); + + for(CkanTag ckanTag : tags){ + if(!ckanTag.getName().equals(bean.getCurrentStatus().toString())){ + JSONObject obj = new JSONObject(); + obj.put("vocabulary_id", ckanTag.getVocabularyId()); + obj.put("state", ckanTag.getState().toString()); + obj.put("display_name", ckanTag.getDisplayName()); + obj.put("id", ckanTag.getId()); + obj.put("name", ckanTag.getName()); + tagsAsJson.add(obj); + } + } + + // add the new one + JSONObject newTag = new JSONObject(); + newTag.put("name", bean.getNewStatus().toString()); + tagsAsJson.add(new JSONObject()); + + // manage the groups + List groups = dataset.getGroups(); + for (CkanGroup ckanGroup : groups) { + if(!ckanGroup.getName().equals("grsf" + "-" + bean.getCurrentStatus().toString().toLowerCase())){ + JSONObject obj = new JSONObject(); + obj.put("name", ckanGroup.getName()); + groupsAsJson.add(obj); + } + } + + JSONObject newGroup = new JSONObject(); + newGroup.put("name", "grsf" + "-" + bean.getNewStatus().toString().toLowerCase()); + groupsAsJson.add(newGroup); + + // perform the request + jsonRequest.put("id", bean.getCatalogueIdentifier()); + jsonRequest.put("tags", tagsAsJson); + jsonRequest.put("extras", customFieldsAsJson); + jsonRequest.put("groups", groupsAsJson); + + logger.debug("Request param is going to be " + jsonRequest); + + if((errorMessage = catalogue.patchProductWithJSON(bean.getCatalogueIdentifier(), jsonRequest, apiKey)) == null){ + logger.info("Record patched ..."); + }else + continue; // retry + + }catch(Exception e){ + logger.error("Error while trying to patch grsf record (iteration " + i + " of " + MAX_TRIAL + ")" + e.getMessage()); + errorMessage = e.getMessage(); + } + + return errorMessage; + } + } } diff --git a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/server/manage/PatchProductThread.java b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/server/manage/PatchProductThread.java deleted file mode 100644 index 2b5a5f7..0000000 --- a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/server/manage/PatchProductThread.java +++ /dev/null @@ -1,67 +0,0 @@ -package org.gcube.portlets.gcubeckan.gcubeckandatacatalog.server.manage; - -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue; -import org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.ManageProductBean; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Thread used to patch a ckan product. - * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) - */ -public class PatchProductThread extends Thread{ - - private static Logger logger = LoggerFactory.getLogger(PatchProductThread.class); - private DataCatalogue catalogue; - private ManageProductBean bean; - private String username; - private static final String ANNOTATION_KEY = "Annotation on Status update"; - private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); - private static final String STATUS_CUSTOM_FIELD_KEY = "Status"; - - /** - * @param catalogue - * @param bean - */ - public PatchProductThread(DataCatalogue catalogue, ManageProductBean bean, String username) { - super(); - this.catalogue = catalogue; - this.bean = bean; - this.username = username; - } - - @Override - public void run() { - - logger.info("Going to patch record in the catalogue with identifier " + bean.getCatalogueIdentifier() + - " from user " + username); - - String apiKey = catalogue.getApiKeyFromUsername(username); - - // remove the "Status" field - catalogue.removeCustomField(bean.getCatalogueIdentifier(), STATUS_CUSTOM_FIELD_KEY, bean.getCurrentStatus().toString(), apiKey); - catalogue.removeTag(bean.getCatalogueIdentifier(), apiKey, bean.getCurrentStatus().toString()); - catalogue.removeDatasetFromGroup("grsf" + "-" + bean.getCurrentStatus().toString().toLowerCase(), bean.getCatalogueIdentifier(), apiKey); - - Map> statusMap = new HashMap>(); - statusMap.put(STATUS_CUSTOM_FIELD_KEY, Arrays.asList(bean.getNewStatus().toString())); - statusMap.put(ANNOTATION_KEY, Arrays.asList("date: " + DATE_FORMAT.format(new Date()) - + ", admin: " + username - + ", annotation: " + (bean.getAnnotation() != null ? bean.getAnnotation().replaceAll("\"", "") : "none") - + ", new status: " + bean.getNewStatus().toString() - + ", old status: " + bean.getCurrentStatus().toString() - )); - catalogue.patchProductCustomFields(bean.getCatalogueIdentifier(), apiKey, statusMap); - catalogue.addTag(bean.getCatalogueIdentifier(), apiKey, bean.getNewStatus().toString()); - catalogue.assignDatasetToGroup("grsf" + "-" + bean.getNewStatus().toString().toLowerCase(), bean.getCatalogueIdentifier(), apiKey); - logger.info("Record patched ..."); - } - -} diff --git a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/shared/ex/NoGRSFRecordException.java b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/shared/ex/NoGRSFRecordException.java index f11d80e..e6dc02a 100644 --- a/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/shared/ex/NoGRSFRecordException.java +++ b/src/main/java/org/gcube/portlets/gcubeckan/gcubeckandatacatalog/shared/ex/NoGRSFRecordException.java @@ -3,7 +3,7 @@ package org.gcube.portlets.gcubeckan.gcubeckandatacatalog.shared.ex; /** * This is thrown when the Manage product is pushed on a product that has - * a Record Type field of Source. + * a Record Type field of Source or none. * @author Costantino Perciante at ISTI-CNR * (costantino.perciante@isti.cnr.it) */