Notification of GRSF update is now performed in one shot to avoid problems. The patch operation is performed at most 5 times in case of errors.

git-svn-id: http://svn.d4science-ii.research-infrastructures.eu/gcube/trunk/portlets/user/gcube-ckan-datacatalog@139872 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
Costantino Perciante 2016-12-10 09:52:38 +00:00
parent 8e31ba287b
commit a1874a714e
7 changed files with 135 additions and 83 deletions

View File

@ -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

View File

@ -91,7 +91,7 @@ public interface GcubeCkanDataCatalogServiceAsync {
* Notify product update
*/
void notifyProductUpdate(ManageProductBean bean,
AsyncCallback<Boolean> callback);
AsyncCallback<String> callback);
void getProductBeanById(String identifier,
AsyncCallback<ManageProductBean> callback);

View File

@ -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<Boolean>() {
service.notifyProductUpdate(bean, new AsyncCallback<String>() {
@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);
}

View File

@ -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

View File

@ -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<CkanPair> 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<CkanTag> 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<CkanGroup> 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;
}
}
}

View File

@ -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<String, List<String>> statusMap = new HashMap<String, List<String>>();
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 ...");
}
}

View File

@ -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)
*/