2016-10-07 18:23:23 +02:00
package org.gcube.data_catalogue.grsf_publish_ws.utils ;
2016-10-10 18:50:11 +02:00
import java.io.BufferedReader ;
2016-11-28 17:43:28 +01:00
import java.io.File ;
2016-10-10 18:50:11 +02:00
import java.io.InputStreamReader ;
2016-11-30 14:14:02 +01:00
import java.io.UnsupportedEncodingException ;
2016-10-11 11:39:25 +02:00
import java.util.HashMap ;
2016-10-08 23:10:25 +02:00
import java.util.List ;
import java.util.Map ;
2016-12-09 14:06:19 +01:00
import org.gcube.common.authorization.library.provider.SecurityTokenProvider ;
2016-11-28 17:43:28 +01:00
import org.gcube.common.homelibrary.home.exceptions.InternalErrorException ;
import org.gcube.common.homelibrary.home.workspace.WorkspaceFolder ;
import org.gcube.common.homelibrary.home.workspace.WorkspaceItem ;
import org.gcube.common.homelibrary.home.workspace.exceptions.InsufficientPrivilegesException ;
import org.gcube.common.homelibrary.home.workspace.exceptions.ItemAlreadyExistException ;
import org.gcube.common.homelibrary.home.workspace.folder.items.ExternalFile ;
2016-12-09 14:06:19 +01:00
import org.gcube.common.scope.api.ScopeProvider ;
2016-12-16 18:53:08 +01:00
import org.gcube.data_catalogue.grsf_publish_ws.utils.cache.CacheImpl ;
import org.gcube.data_catalogue.grsf_publish_ws.utils.cache.CacheInterface ;
2016-10-08 22:16:31 +02:00
import org.gcube.datacatalogue.ckanutillibrary.DataCatalogue ;
import org.gcube.datacatalogue.ckanutillibrary.DataCatalogueFactory ;
2016-10-10 18:50:11 +02:00
import org.gcube.datacatalogue.ckanutillibrary.DataCatalogueImpl ;
2016-11-30 11:43:30 +01:00
import org.jsoup.Jsoup ;
2016-11-30 14:14:02 +01:00
import org.jsoup.safety.Whitelist ;
2016-10-08 23:10:25 +02:00
import org.slf4j.LoggerFactory ;
2016-10-08 22:16:31 +02:00
2016-12-09 14:06:19 +01:00
import com.fasterxml.jackson.databind.ObjectMapper ;
import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpEntity ;
2016-10-10 18:50:11 +02:00
import eu.trentorise.opendata.jackan.internal.org.apache.http.HttpResponse ;
import eu.trentorise.opendata.jackan.internal.org.apache.http.client.methods.HttpGet ;
2016-12-09 14:06:19 +01:00
import eu.trentorise.opendata.jackan.internal.org.apache.http.client.methods.HttpPost ;
import eu.trentorise.opendata.jackan.internal.org.apache.http.entity.StringEntity ;
2016-10-10 18:50:11 +02:00
import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.CloseableHttpClient ;
import eu.trentorise.opendata.jackan.internal.org.apache.http.impl.client.HttpClientBuilder ;
2016-12-09 14:06:19 +01:00
import eu.trentorise.opendata.jackan.internal.org.apache.http.util.EntityUtils ;
2016-10-11 11:39:25 +02:00
import eu.trentorise.opendata.jackan.model.CkanLicense ;
2016-10-10 18:50:11 +02:00
2016-10-07 18:23:23 +02:00
/ * *
* Helper methods
* @author Costantino Perciante at ISTI - CNR ( costantino . perciante @isti.cnr.it )
* /
public abstract class HelperMethods {
2016-10-08 23:10:25 +02:00
private static final org . slf4j . Logger logger = LoggerFactory . getLogger ( HelperMethods . class ) ;
2016-12-09 14:06:19 +01:00
private static final String APPLICATION_ID_CATALOGUE_MANAGER = " org.gcube.datacatalogue.ProductCatalogue " ;
private static final String NOTIFICATION_MESSAGE = " Dear members,<br>The product 'PRODUCT_TITLE' has been just published by USER_FULLNAME.<br>You can find it here: PRODUCT_URL <br> " ;
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 " ;
2016-12-16 18:53:08 +01:00
2016-10-10 18:50:11 +02:00
// to be retrieved from the web.xml
2016-12-06 11:16:31 +01:00
public static final String MANAGE_CONTEX_KEY = " ManageVRE " ;
public static final String PUBLIC_CONTEX_KEY = " PublicVRE " ;
2016-11-28 17:43:28 +01:00
private static final String CSV_MIME = " text/csv " ;
private static final String PATH_SEPARATOR = " / " ;
2016-12-01 22:20:46 +01:00
2016-12-16 18:53:08 +01:00
// caches
private static CacheInterface userEmailCache = new CacheImpl ( 1000 * 60 * 30 ) ;
private static CacheInterface userFullnameCache = new CacheImpl ( 1000 * 60 * 30 ) ;
2016-10-08 22:16:31 +02:00
/ * *
* Convert a group name to its id on ckan
* @param origName
* @return
* /
2016-10-07 18:23:23 +02:00
public static String getGroupNameOnCkan ( String origName ) {
2016-10-08 23:10:25 +02:00
2016-10-07 18:23:23 +02:00
if ( origName = = null )
throw new IllegalArgumentException ( " origName cannot be null " ) ;
2016-10-08 23:10:25 +02:00
2016-10-10 18:50:11 +02:00
String modified = origName . trim ( ) . toLowerCase ( ) . replaceAll ( " [^A-Za-z0-9-] " , " - " ) ;
if ( modified . startsWith ( " - " ) )
modified = modified . substring ( 1 ) ;
if ( modified . endsWith ( " - " ) )
modified = modified . substring ( 0 , modified . length ( ) - 1 ) ;
2016-12-01 15:37:26 +01:00
2016-11-30 18:43:57 +01:00
logger . info ( " Group name generated is " + modified ) ;
2016-10-11 11:39:25 +02:00
2016-10-10 18:50:11 +02:00
return modified ;
2016-10-07 18:23:23 +02:00
}
2016-10-08 23:10:25 +02:00
2016-10-08 22:16:31 +02:00
/ * *
* Retrieve the running instance of the data catalogue for this scope
* @return
* @throws Exception
* /
2016-11-04 16:26:19 +01:00
public static DataCatalogue getDataCatalogueRunningInstance ( String scope ) {
2016-10-08 23:10:25 +02:00
2016-10-10 18:50:11 +02:00
try {
DataCatalogueImpl instance = DataCatalogueFactory . getFactory ( ) . getUtilsPerScope ( scope ) ;
return instance ;
} catch ( Exception e ) {
logger . error ( " Failed to instanciate data catalogue lib " , e ) ;
}
return null ;
2016-10-08 23:10:25 +02:00
}
2016-10-10 18:50:11 +02:00
/ * *
* Retrieve the organization name in which the user wants to publish starting from the scope
* @param contextInWhichPublish
* @return
* /
public static String retrieveOrgNameFromScope ( String scope ) {
String [ ] splittedScope = scope . split ( " / " ) ;
return splittedScope [ splittedScope . length - 1 ] . toLowerCase ( ) ;
}
/ * *
* Validate the name the product will have
* @param futureName
* @return
* /
2016-11-06 21:21:11 +01:00
public static boolean isNameValid ( String futureName ) {
2016-10-10 18:50:11 +02:00
if ( futureName = = null | | futureName . isEmpty ( ) )
return false ;
else {
return futureName . matches ( " [ \\ sA-Za-z0-9_.-]+ " ) ;
}
}
/ * *
* Retrieve the user ' s email given his / her username
* @param context
* @param token
* @return
* @throws Exception
* /
2016-12-16 18:53:08 +01:00
@SuppressWarnings ( " unchecked " )
2016-10-10 18:50:11 +02:00
public static String getUserEmail ( String context , String token ) {
2016-12-16 18:53:08 +01:00
// check in cache
String result = null ;
if ( ( result = ( String ) userEmailCache . get ( token ) ) ! = null ) {
return result ;
} else {
String baseUrl = new ServiceEndPointReaderSocial ( context ) . getBasePath ( ) ;
String url = baseUrl + " users/getUserEmail?gcube-token= " + token ;
logger . debug ( " Request url is " + url ) ;
result = executGETHttpRequest ( url , 200 ) ;
userEmailCache . insert ( token , result ) ;
}
return result ;
2016-10-10 18:50:11 +02:00
}
/ * *
* Retrieve the user ' s fullname given his / her username
* @param context
* @param token
* @return
* @throws Exception
* /
2016-12-16 18:53:08 +01:00
@SuppressWarnings ( " unchecked " )
2016-10-10 18:50:11 +02:00
public static String getUserFullname ( String context , String token ) {
2016-12-16 18:53:08 +01:00
// check in cache
String result = null ;
if ( ( result = ( String ) userFullnameCache . get ( token ) ) ! = null ) {
return result ;
} else {
String baseUrl = new ServiceEndPointReaderSocial ( context ) . getBasePath ( ) ;
String url = baseUrl + " users/getUserFullname?gcube-token= " + token ;
logger . debug ( " Request url is " + url ) ;
result = executGETHttpRequest ( url , 200 ) ;
userFullnameCache . insert ( token , result ) ;
}
return result ;
2016-11-06 21:21:11 +01:00
}
/ * *
* Execute the GET http request at this url , and return the result as string
* @return
* /
private static String executGETHttpRequest ( String url , int expectedCodeOnSuccess ) {
2016-10-10 18:50:11 +02:00
try ( CloseableHttpClient client = HttpClientBuilder . create ( ) . build ( ) ; ) {
HttpGet getRequest = new HttpGet ( url ) ;
HttpResponse response = client . execute ( getRequest ) ;
2016-11-06 21:21:11 +01:00
if ( response . getStatusLine ( ) . getStatusCode ( ) ! = expectedCodeOnSuccess ) {
2016-10-10 18:50:11 +02:00
throw new RuntimeException ( " Failed : HTTP error code : "
+ response . getStatusLine ( ) . getStatusCode ( ) ) ;
}
BufferedReader br = new BufferedReader (
new InputStreamReader ( ( response . getEntity ( ) . getContent ( ) ) ) ) ;
2016-11-06 21:21:11 +01:00
String res = " " ;
2016-10-10 18:50:11 +02:00
String temp = null ;
while ( ( temp = br . readLine ( ) ) ! = null ) {
2016-11-06 21:21:11 +01:00
res + = temp ;
2016-10-10 18:50:11 +02:00
}
2016-11-06 21:21:11 +01:00
return res ;
2016-10-10 18:50:11 +02:00
} catch ( Exception e ) {
2016-11-30 11:43:30 +01:00
logger . error ( " error while performing get method " + e . toString ( ) ) ;
2016-10-10 18:50:11 +02:00
}
return null ;
2016-11-06 21:21:11 +01:00
2016-10-10 18:50:11 +02:00
}
2016-10-11 11:39:25 +02:00
/ * *
* Retrieve the list of ckan licenses and build up a map < license_id , license_title >
* @return
* @throws Exception
* /
2016-11-06 21:21:11 +01:00
public static Map < String , String > getLicenses ( DataCatalogue catalogue ) throws Exception {
2016-10-11 11:39:25 +02:00
Map < String , String > toReturn = new HashMap < String , String > ( ) ;
List < CkanLicense > licenses = catalogue . getLicenses ( ) ;
for ( CkanLicense ckanLicense : licenses ) {
toReturn . put ( ckanLicense . getId ( ) , ckanLicense . getTitle ( ) ) ;
}
return toReturn ;
}
/ * *
* Check that the given license id is in CKAN
* @param license id to check
* @return
* @throws Exception
* /
2016-11-06 21:21:11 +01:00
public static boolean existsLicenseId ( String license , DataCatalogue catalogue ) throws Exception {
2016-10-11 11:39:25 +02:00
2016-11-04 16:26:19 +01:00
List < CkanLicense > licenses = catalogue . getLicenses ( ) ;
for ( CkanLicense ckanLicense : licenses ) {
if ( ckanLicense . getId ( ) . equals ( license ) )
return true ;
}
return false ;
2016-10-11 11:39:25 +02:00
}
2016-11-28 17:43:28 +01:00
/ * *
* Upload a file in the shared folder
* @param resourceFormatFolder
* @param resourceToAttachName
* @param description
* @param csvFile
* @return
* /
public static ExternalFile uploadExternalFile ( WorkspaceFolder resourceFormatFolder , String resourceToAttachName , String description , File csvFile ) {
try {
return resourceFormatFolder . createExternalFileItem ( resourceToAttachName , description , CSV_MIME , csvFile ) ;
} catch ( InsufficientPrivilegesException | ItemAlreadyExistException
| InternalErrorException e ) {
logger . error ( " Failed to upload the file into the workspace shared folder for " + resourceToAttachName , e ) ;
}
return null ;
}
/ * *
* Create subfolders in cascade , returning the last created ones
* It could be also used for getting them if they already exists
* @param folder
* @param subPath
* @return null if an error occurred
* /
public static WorkspaceFolder createOrGetSubFoldersByPath ( WorkspaceFolder folder , String subPath ) {
WorkspaceFolder parentFolder = folder ;
if ( folder = = null )
throw new IllegalArgumentException ( " Root folder is null! " ) ;
if ( subPath = = null | | subPath . isEmpty ( ) )
throw new IllegalArgumentException ( " subPath is null/empty! " ) ;
try {
if ( subPath . startsWith ( PATH_SEPARATOR ) )
subPath = subPath . replaceFirst ( PATH_SEPARATOR , " " ) ;
if ( subPath . endsWith ( PATH_SEPARATOR ) )
subPath = subPath . substring ( 0 , subPath . length ( ) - 1 ) ;
logger . debug ( " Splitting path " + subPath ) ;
String [ ] splittedPaths = subPath . split ( PATH_SEPARATOR ) ;
for ( String path : splittedPaths ) {
WorkspaceFolder createdFolder = getFolderOrCreate ( parentFolder , path , " " ) ;
logger . debug ( " Created subfolder with path " + createdFolder . getPath ( ) ) ;
parentFolder = createdFolder ;
}
} catch ( Exception e ) {
logger . error ( " Failed to create the subfolders by path " + subPath ) ;
return null ;
}
return parentFolder ;
}
/ * *
* Get a folder within the catalogue folder or create it if it doesn ' t exist .
* @return
* /
public static WorkspaceFolder getFolderOrCreate ( WorkspaceFolder folder , String relativePath , String descriptionFolder ) {
WorkspaceFolder result = null ;
try {
WorkspaceItem foundFolder = folder . find ( relativePath ) ;
if ( foundFolder ! = null & & foundFolder . isFolder ( ) )
result = ( WorkspaceFolder ) foundFolder ;
if ( result ! = null )
logger . debug ( " Folder found with name " + result . getName ( ) + " , it has id " + result . getId ( ) ) ;
else
throw new Exception ( " There is no folder with name " + relativePath + " under folder " + folder . getName ( ) ) ;
} catch ( Exception e ) {
logger . debug ( " Probably the folder doesn't exist " ) ;
try {
result = folder . createFolder ( relativePath , descriptionFolder ) ;
} catch ( InsufficientPrivilegesException | InternalErrorException | ItemAlreadyExistException e2 ) {
logger . error ( " Failed to get or generate this folder " , e2 ) ;
}
}
return result ;
}
2016-11-30 14:14:02 +01:00
2016-11-30 11:43:30 +01:00
/ * *
* Strip out HTML code
* @param html
* @return
2016-11-30 14:14:02 +01:00
* @throws UnsupportedEncodingException
2016-11-30 11:43:30 +01:00
* /
public static String removeHTML ( String html ) {
2016-11-30 11:50:47 +01:00
if ( html = = null | | html . isEmpty ( ) )
return html ;
2016-11-30 14:14:02 +01:00
// remove html and clean
2016-11-30 11:43:30 +01:00
String withoutHTML = Jsoup . parse ( html ) . text ( ) ;
2016-11-30 14:14:02 +01:00
withoutHTML = Jsoup . clean ( withoutHTML , Whitelist . basic ( ) ) ;
// remove non ascii chars ...
withoutHTML = withoutHTML . replaceAll ( " [^ \\ p{ASCII}] " , " " ) ;
return withoutHTML ;
2016-11-30 11:43:30 +01:00
}
2016-12-16 18:53:08 +01:00
2016-12-09 14:06:19 +01:00
/ * *
* 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 < String > 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 ServiceEndPointReaderSocial ( currentScope ) . getBasePath ( ) ;
if ( basePath = = null ) {
logger . error ( " Unable to write a post because there is no social networking service available " ) ;
} else {
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 ) ;
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 < String , Object > 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 < String , Object > 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 < String , Object > getResponseEntityAsJSON ( HttpResponse response ) {
Map < String , Object > toReturn = null ;
HttpEntity entity = response . getEntity ( ) ;
if ( entity ! = null ) {
try {
toReturn = new HashMap < String , Object > ( ) ;
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 ;
}
2016-10-07 18:23:23 +02:00
}