From fcc36f6b47caeb124576a393244f015756bba953 Mon Sep 17 00:00:00 2001 From: Costantino Perciante Date: Tue, 28 Feb 2017 11:36:29 +0000 Subject: [PATCH] minor fix on write post about item git-svn-id: http://svn.d4science-ii.research-infrastructures.eu/gcube/trunk/portlets/widgets/ckan-metadata-publisher-widget@144396 82a268e6-3cf1-43bd-a215-b396298e98cf --- .../client/ui/CreateDatasetForm.java | 2 +- .../resources/AddResourceToDataset.java | 2 +- .../WritePostCatalogueManagerThread.java | 218 +++++++++++++++++- .../server/utils/Utils.java | 158 ------------- .../server/utils/WorkspaceUtils.java | 28 ++- 5 files changed, 243 insertions(+), 165 deletions(-) diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.java index da73973..174732e 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/CreateDatasetForm.java @@ -922,7 +922,7 @@ public class CreateDatasetForm extends Composite{ (datasetUrl.length() > 100 ? datasetUrl.substring(0, 100) + "..." : datasetUrl) ); - goToDatasetButton.setHref(datasetUrl); + // goToDatasetButton.setHref(datasetUrl); goToDatasetButton.addClickHandler(new ClickHandler() { @Override diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/dataset/resources/AddResourceToDataset.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/dataset/resources/AddResourceToDataset.java index de6379f..aed866e 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/dataset/resources/AddResourceToDataset.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/client/ui/dataset/resources/AddResourceToDataset.java @@ -68,7 +68,7 @@ public class AddResourceToDataset extends Composite{ (datasetUrl.length() > 100 ? datasetUrl.substring(0, 100) + "..." : datasetUrl) ); - goToDatasetButton.setHref(datasetUrl); + // goToDatasetButton.setHref(datasetUrl); goToDatasetButton.addClickHandler(new ClickHandler() { @Override diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/threads/WritePostCatalogueManagerThread.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/threads/WritePostCatalogueManagerThread.java index d2a2319..393f572 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/threads/WritePostCatalogueManagerThread.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/threads/WritePostCatalogueManagerThread.java @@ -1,14 +1,30 @@ package org.gcube.portlets.widgets.ckandatapublisherwidget.server.threads; +import java.io.IOException; +import java.net.HttpURLConnection; import java.util.List; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.scope.api.ScopeProvider; +import org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils.GCoreEndPointReaderSocial; +import org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils.ServiceEndPointReaderSocial; import org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils.Utils; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; +import eu.trentorise.opendata.jackan.internal.org.apache.http.Header; +import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpEntity; +import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpResponse; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.ClientProtocolException; +import eu.trentorise.opendata.jackan.internal.org.apache.http.client.methods.HttpPost; +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; + /** * Let the Product Catalogue Manager write a post in a VRE and alert there is a new product @@ -17,15 +33,20 @@ import com.liferay.portal.kernel.log.LogFactoryUtil; */ public class WritePostCatalogueManagerThread extends Thread { - //private static final Logger logger = LoggerFactory.getLogger(WritePostCatalogueManagerThread.class); + private static final String APPLICATION_ID_CATALOGUE_MANAGER = "org.gcube.datacatalogue.ProductCatalogue"; + private static final String NOTIFICATION_MESSAGE = "Dear members,
The item '$PRODUCT_TITLE' has been just published by $USER_FULLNAME.
You can find it here: $PRODUCT_URL
"; + private static final String SOCIAL_SERVICE_APPLICATION_TOKEN = "/2/tokens/generate-application-token"; + private static final String SOCIAL_SERVICE_WRITE_APPLICATION_POST = "/2/posts/write-post-app"; + private static final String MEDIATYPE_JSON = "application/json"; private static final Log logger = LogFactoryUtil.getLog(WritePostCatalogueManagerThread.class); + // private static Logger logger = LoggerFactory.getLogger(WritePostCatalogueManagerThread.class); private String username; private String scope; private String productTitle; private String productUrl; private boolean enableNotification; private List hashtags; - String userFullName; + private String userFullName; /** * @param token @@ -56,7 +77,7 @@ public class WritePostCatalogueManagerThread extends Thread { try{ // evaluate user's token for this scope String token = Utils.tryGetElseCreateToken(username, scope); - + if(token == null){ logger.warn("Unable to proceed, user's token is not available"); return; @@ -71,7 +92,7 @@ public class WritePostCatalogueManagerThread extends Thread { SecurityTokenProvider.instance.set(token); // write - Utils.writeProductPost( + writeProductPost( productTitle, productUrl, userFullName, @@ -86,4 +107,193 @@ public class WritePostCatalogueManagerThread extends Thread { ScopeProvider.instance.reset(); } } + + /** + * Send notification to vre members about the created product by writing a post. + * @param productName the title of the product + * @param productUrl the url of the product + * @param hashtags a list of product's hashtags + */ + private static void writeProductPost(String productName, String productUrl, String userFullname, List hashtags, boolean enablePostNotification){ + + // discover service endpoint for the social networking library + String currentScope = ScopeProvider.instance.get(); + String tokenUser = SecurityTokenProvider.instance.get(); + + logger.info("Current scope for writeProductPost is " + currentScope + " and token is " + tokenUser.substring(0, 10) + "***************"); + String basePath = new GCoreEndPointReaderSocial(currentScope).getBasePath(); + + // add fallback + if(basePath == null || basePath.isEmpty()) + new ServiceEndPointReaderSocial(currentScope).getBasePath(); + + if(basePath == null){ + + logger.error("Unable to write a post because there is no social networking service available"); + + }else{ + + // check base path form + basePath = basePath.endsWith("/") ? basePath : basePath + "/"; + + try(CloseableHttpClient client = HttpClientBuilder.create().build();){ + + String pathTokenApp = basePath + SOCIAL_SERVICE_APPLICATION_TOKEN + "?gcube-token=" + tokenUser; + String tokenApp = requireAppToken(client, pathTokenApp); + if(tokenApp != null){ + String pathWritePost = basePath + SOCIAL_SERVICE_WRITE_APPLICATION_POST + "?gcube-token=" + tokenApp; + writePost(client, pathWritePost, productName, productUrl, userFullname, hashtags, enablePostNotification); + } + + }catch(Exception e){ + logger.error("Failed to create a post", e); + } + } + } + + /** + * Require the application token + * @param tokenUser + * @param basePath + * @param client + * @return + */ + private static String requireAppToken(CloseableHttpClient client, String path){ + + String token = null; + try{ + + HttpResponse response = performRequest(client, path, "{\"app_id\":\"" + APPLICATION_ID_CATALOGUE_MANAGER + "\"}"); + + int statusTokenGenerate = response.getStatusLine().getStatusCode(); + + if(statusTokenGenerate == HttpURLConnection.HTTP_CREATED){ + + // extract token + JSONObject obj = getJSONObject(response); + if(((Boolean) obj.get("success"))) + token = (String)obj.get("result"); + else + return null; + + }else if(statusTokenGenerate == HttpURLConnection.HTTP_MOVED_TEMP + || statusTokenGenerate == HttpURLConnection.HTTP_MOVED_PERM + || statusTokenGenerate == HttpURLConnection.HTTP_SEE_OTHER){ + + // re-execute + Header[] locations = response.getHeaders("Location"); + Header lastLocation = locations[locations.length - 1]; + String realLocation = lastLocation.getValue(); + logger.debug("New location is " + realLocation); + token = requireAppToken(client, realLocation); + + }else + return null; + + }catch(Exception e){ + logger.error("Failed to retrieve application token", e); + } + + logger.info("Returning app token " + (token != null ? token.substring(0, 10) + "*************************" : null)); + return token; + } + + /** + * Write post request + * @param client + * @param applicationToken + * @param productName + * @param productUrl + * @param userFullname + * @param hashtags + */ + private static void writePost(CloseableHttpClient client, String path, String productName, String productUrl, String userFullname, List hashtags, + boolean enablePostNotification) { + + try{ + + // replace + String message = NOTIFICATION_MESSAGE.replace("$PRODUCT_TITLE", productName).replace("$PRODUCT_URL", productUrl).replace("$USER_FULLNAME", userFullname); + + if(hashtags != null && !hashtags.isEmpty()) + for (String hashtag : hashtags) { + String modifiedHashtag = hashtag.replaceAll(" ", "_").replace("_+", "_"); + if(modifiedHashtag.endsWith("_")) + modifiedHashtag = modifiedHashtag.substring(0, modifiedHashtag.length() - 1); + message += " #" + modifiedHashtag; // ckan accepts tag with empty spaces, we don't + } + + logger.info("The post that is going to be written is -> " + message); + + HttpResponse response = performRequest(client, path, "{\"text\":\"" + message + "\", \"enable_notification\" : "+ enablePostNotification+ "}"); + int statusWritePost = response.getStatusLine().getStatusCode(); + + if(statusWritePost == HttpURLConnection.HTTP_CREATED){ + + // extract token + JSONObject obj = getJSONObject(response); + if(((Boolean) obj.get("success"))) + logger.info("Post written"); + else + logger.info("Failed to write the post " + obj.get("message")); + + }else if(statusWritePost == HttpURLConnection.HTTP_MOVED_TEMP + || statusWritePost == HttpURLConnection.HTTP_MOVED_PERM + || statusWritePost == HttpURLConnection.HTTP_SEE_OTHER){ + + // re-execute + Header[] locations = response.getHeaders("Location"); + Header lastLocation = locations[locations.length - 1]; + String realLocation = lastLocation.getValue(); + logger.debug("New location is " + realLocation); + writePost(client, realLocation, productName, productUrl, userFullname, hashtags, enablePostNotification); + + }else + throw new RuntimeException("Failed to write the post"); + + }catch(Exception e){ + logger.error("Failed to retrieve application token", e); + } + + } + + /** + * Convert the json response to a map + * @param response + * @return + */ + private static JSONObject getJSONObject(HttpResponse response){ + + JSONObject toReturn = null; + HttpEntity entity = response.getEntity(); + + if (entity != null) { + try { + String jsonString = EntityUtils.toString(response.getEntity()); + JSONParser parser = new JSONParser(); + toReturn = (JSONObject)parser.parse(jsonString); + }catch(Exception e){ + logger.error("Failed to read json object", e); + } + } + + logger.debug("Returning " + toReturn.toJSONString()); + return toReturn; + } + + /** + * Perform an http request post request with json entity + * @throws IOException + * @throws ClientProtocolException + */ + private static HttpResponse performRequest(CloseableHttpClient client, String path, String entity) throws ClientProtocolException, IOException{ + + HttpPost request = new HttpPost(path); + StringEntity stringEntity = new StringEntity(entity); + stringEntity.setContentType(MEDIATYPE_JSON); + request.setEntity(stringEntity); + return client.execute(request); + + } + } \ No newline at end of file diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/Utils.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/Utils.java index e0b0ed2..fc51266 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/Utils.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/Utils.java @@ -3,9 +3,7 @@ package org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils; import static org.gcube.common.authorization.client.Constants.authorizationService; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @@ -13,9 +11,6 @@ import javax.servlet.http.HttpSession; import org.gcube.common.authorization.client.exceptions.ObjectNotFound; import org.gcube.common.authorization.library.provider.SecurityTokenProvider; import org.gcube.common.authorization.library.provider.UserInfo; -import org.gcube.common.homelibrary.home.exceptions.InternalErrorException; -import org.gcube.common.homelibrary.home.workspace.WorkspaceItem; -import org.gcube.common.homelibrary.home.workspace.folder.items.GCubeItem; import org.gcube.common.portal.PortalContext; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.datacatalogue.ckanutillibrary.server.ApplicationProfileScopePerUrlReader; @@ -48,17 +43,9 @@ import org.gcube.vomanagement.usermanagement.model.GCubeRole; import org.gcube.vomanagement.usermanagement.model.GCubeUser; import org.gcube.vomanagement.usermanagement.model.GatewayRolesNames; -import com.fasterxml.jackson.databind.ObjectMapper; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; -import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpEntity; -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.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.CkanOrganization; /** @@ -70,38 +57,8 @@ public class Utils { // Logger //private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Utils.class); private static final Log logger = LogFactoryUtil.getLog(Utils.class); - private static final String APPLICATION_ID_CATALOGUE_MANAGER = "org.gcube.datacatalogue.ProductCatalogue"; - private static final String NOTIFICATION_MESSAGE = "Dear members,
The item 'PRODUCT_TITLE' has been just published by USER_FULLNAME.
You can find it here: PRODUCT_URL
"; - private static final String SOCIAL_SERVICE_APPLICATION_TOKEN = "/2/tokens/generate-application-token"; - private static final String SOCIAL_SERVICE_WRITE_APPLICATION_POST = "/2/posts/write-post-app"; - private static final String MEDIATYPE_JSON = "application/json"; public static final String GCUBE_REQUEST_URL = "gcube-request-url"; - /** Gets the gcube item properties. - * - * @param item the item - * @return the gcube item properties - */ - public static Map getGcubeItemProperties(WorkspaceItem item) { - - if(item instanceof GCubeItem){ - GCubeItem gItem = (GCubeItem) item; - try { - if(gItem.getProperties()!=null){ - Map map = gItem.getProperties().getProperties(); - HashMap properties = new HashMap(map.size()); //TO PREVENT GWT SERIALIZATION ERROR - for (String key : map.keySet()) - properties.put(key, map.get(key)); - - return properties; - } - } catch (InternalErrorException e) { - logger.error("Error in server getItemProperties: ", e); - } - } - return null; - } - /** * Retrieve the highest ckan role the user has and also retrieve the list of organizations (scopes) in which the user has the ckan-admin or ckan-editor role * @param currentScope the current scope @@ -385,121 +342,6 @@ public class Utils { return beans; } - /** - * Send notification to vre members about the created product by writing a post. - * @param productName the title of the product - * @param productUrl the url of the product - * @param hashtags a list of product's hashtags - */ - public static void writeProductPost(String productName, String productUrl, String userFullname, List hashtags, boolean enablePostNotification){ - - // discover service endpoint for the social networking library - String currentScope = ScopeProvider.instance.get(); - String tokenUser = SecurityTokenProvider.instance.get(); - - logger.info("Current scope for writeProductPost is " + currentScope + " and token is " + tokenUser.substring(0, 10) + "***************"); - String basePath = new GCoreEndPointReaderSocial(currentScope).getBasePath(); - - // add fallback - if(basePath == null || basePath.isEmpty()) - new ServiceEndPointReaderSocial(currentScope).getBasePath(); - - if(basePath == null){ - - logger.error("Unable to write a post because there is no social networking service available"); - - }else{ - - // check base path form - basePath = basePath.endsWith("/") ? basePath : basePath + "/"; - - try(CloseableHttpClient client = HttpClientBuilder.create().build();){ - - // ask token application - HttpPost postRequest = new HttpPost(basePath + SOCIAL_SERVICE_APPLICATION_TOKEN + "?gcube-token=" + tokenUser); - StringEntity input = new StringEntity("{\"app_id\":\"" + APPLICATION_ID_CATALOGUE_MANAGER + "\"}"); - input.setContentType(MEDIATYPE_JSON); - postRequest.setEntity(input); - HttpResponse response = client.execute(postRequest); - - basePath = basePath.startsWith("https") ? basePath : basePath.replace("http", "https").replace(":80", ""); - logger.debug("Url is " + basePath + SOCIAL_SERVICE_APPLICATION_TOKEN + "?gcube-token=" + tokenUser); - - if (response.getStatusLine().getStatusCode() != 201) { - throw new RuntimeException("Failed to retrieve application token : HTTP error code : " - + response.getStatusLine().getStatusCode()); - }else{ - - Map mapResponseGeneratedToken = getResponseEntityAsJSON(response); - boolean successGeneratedToken = (boolean)mapResponseGeneratedToken.get("success"); - if(!successGeneratedToken){ - - throw new RuntimeException("Failed to generate the token for the application!" - + " Error message is " + mapResponseGeneratedToken.get("message")); - - }else{ - - String applicationToken = (String)mapResponseGeneratedToken.get("result"); - - // replace - String message = NOTIFICATION_MESSAGE.replace("PRODUCT_TITLE", productName).replace("PRODUCT_URL", productUrl).replace("USER_FULLNAME", userFullname); - - if(hashtags != null && !hashtags.isEmpty()) - for (String hashtag : hashtags) { - String modifiedHashtag = hashtag.replaceAll(" ", "_").replace("_+", "_"); - if(modifiedHashtag.endsWith("_")) - modifiedHashtag = modifiedHashtag.substring(0, modifiedHashtag.length() - 1); - message += " #" + modifiedHashtag; // ckan accepts tag with empty spaces, we don't - } - - logger.info("The post that is going to be written is -> " + message); - postRequest = new HttpPost(basePath + SOCIAL_SERVICE_WRITE_APPLICATION_POST + "?gcube-token=" + applicationToken); - input = new StringEntity("{\"text\":\"" + message + "\", \"enable_notification\" : "+ enablePostNotification+ "}"); - input.setContentType(MEDIATYPE_JSON); - postRequest.setEntity(input); - response = client.execute(postRequest); - - Map mapResponseWritePost = getResponseEntityAsJSON(response); - - if (response.getStatusLine().getStatusCode() != 201) - throw new RuntimeException("Failed to write application post : HTTP error code : " - + response.getStatusLine().getStatusCode() + mapResponseWritePost.get("message")); - } - - } - - }catch(Exception e){ - logger.error("Failed to create a post", e); - } - } - } - - /** - * Convert the json response to a map - * @param response - * @return - */ - public static Map getResponseEntityAsJSON(HttpResponse response){ - - Map toReturn = null; - HttpEntity entity = response.getEntity(); - - if (entity != null) { - try { - toReturn = new HashMap(); - String jsonString = EntityUtils.toString(response.getEntity()); - logger.debug("Response as string is " + jsonString); - ObjectMapper objectMapper = new ObjectMapper(); - toReturn = objectMapper.readValue(jsonString, HashMap.class); - logger.debug("Map is " + toReturn); - }catch(Exception e){ - logger.error("Failed to read json object", e); - } - } - - return toReturn; - } - /** * First check to retrieve the token, else create it * @param username diff --git a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/WorkspaceUtils.java b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/WorkspaceUtils.java index 45f1e4e..f476f68 100644 --- a/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/WorkspaceUtils.java +++ b/src/main/java/org/gcube/portlets/widgets/ckandatapublisherwidget/server/utils/WorkspaceUtils.java @@ -15,6 +15,7 @@ import org.gcube.common.homelibrary.home.workspace.WorkspaceFolder; import org.gcube.common.homelibrary.home.workspace.WorkspaceItem; import org.gcube.common.homelibrary.home.workspace.catalogue.WorkspaceCatalogue; import org.gcube.common.homelibrary.home.workspace.folder.FolderItem; +import org.gcube.common.homelibrary.home.workspace.folder.items.GCubeItem; import org.gcube.datacatalogue.ckanutillibrary.server.models.ResourceBean; import org.gcube.datacatalogue.ckanutillibrary.server.utils.UtilMethods; import org.gcube.portlets.widgets.ckandatapublisherwidget.shared.DatasetMetadataBean; @@ -145,7 +146,7 @@ public class WorkspaceUtils { bean.setDescription(originalFolderOrFile.getDescription()); // Create the folder in the catalogue - Map folderItems = Utils.getGcubeItemProperties(originalFolderOrFile); + Map folderItems = getGcubeItemProperties(originalFolderOrFile); if(folderItems != null){ // transform this properties @@ -165,6 +166,31 @@ public class WorkspaceUtils { } } + + /** Gets the gcube item properties. + * + * @param item the item + * @return the gcube item properties + */ + public static Map getGcubeItemProperties(WorkspaceItem item) { + + if(item instanceof GCubeItem){ + GCubeItem gItem = (GCubeItem) item; + try { + if(gItem.getProperties()!=null){ + Map map = gItem.getProperties().getProperties(); + HashMap properties = new HashMap(map.size()); //TO PREVENT GWT SERIALIZATION ERROR + for (String key : map.keySet()) + properties.put(key, map.get(key)); + + return properties; + } + } catch (InternalErrorException e) { + logger.error("Error in server getItemProperties: ", e); + } + } + return null; + } /** * Returns a tree object