2016-11-17 14:08:22 +01:00
package org.gcube.portlets.widgets.ckandatapublisherwidget.server.threads ;
2016-10-28 18:47:40 +02:00
2017-02-28 12:36:29 +01:00
import java.io.IOException ;
import java.net.HttpURLConnection ;
2018-02-09 15:40:21 +01:00
import java.util.Base64 ;
2016-10-28 18:47:40 +02:00
import java.util.List ;
import org.gcube.common.authorization.library.provider.SecurityTokenProvider ;
2018-02-09 15:40:21 +01:00
import org.gcube.common.portal.GCubePortalConstants ;
2016-10-28 18:47:40 +02:00
import org.gcube.common.scope.api.ScopeProvider ;
2018-02-09 15:40:21 +01:00
import org.gcube.portal.databook.client.GCubeSocialNetworking ;
2017-02-28 12:36:29 +01:00
import org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils.GCoreEndPointReaderSocial ;
2017-04-25 15:41:27 +02:00
import org.gcube.portlets.widgets.ckandatapublisherwidget.server.utils.GenericUtils ;
2017-02-28 12:36:29 +01:00
import org.json.simple.JSONObject ;
import org.json.simple.parser.JSONParser ;
2016-12-26 23:15:29 +01:00
import com.liferay.portal.kernel.log.Log ;
import com.liferay.portal.kernel.log.LogFactoryUtil ;
2016-10-28 18:47:40 +02:00
2017-02-28 12:36:29 +01:00
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 ;
2016-10-28 18:47:40 +02:00
/ * *
2016-10-29 09:23:11 +02:00
* Let the Product Catalogue Manager write a post in a VRE and alert there is a new product
2016-10-28 18:47:40 +02:00
* @author Costantino Perciante at ISTI - CNR
* ( costantino . perciante @isti.cnr.it )
* /
public class WritePostCatalogueManagerThread extends Thread {
2018-02-22 16:05:08 +01:00
public static final String APPLICATION_ID_CATALOGUE_MANAGER = " org.gcube.datacatalogue.ProductCatalogue " ;
2017-02-28 12:36:29 +01:00
private static final String NOTIFICATION_MESSAGE = " Dear members,<br>The item '$PRODUCT_TITLE' has been just published by $USER_FULLNAME.<br>You can find it here: $PRODUCT_URL <br> " ;
2018-02-09 15:40:21 +01:00
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 " ;
2017-02-28 12:36:29 +01:00
private static final String MEDIATYPE_JSON = " application/json " ;
2016-12-26 23:15:29 +01:00
private static final Log logger = LogFactoryUtil . getLog ( WritePostCatalogueManagerThread . class ) ;
2016-10-28 18:47:40 +02:00
private String username ;
private String scope ;
private String productTitle ;
private String productUrl ;
private boolean enableNotification ;
private List < String > hashtags ;
2017-02-28 12:36:29 +01:00
private String userFullName ;
2018-02-09 15:40:21 +01:00
private String userCurrentUrl ;
2016-10-28 18:47:40 +02:00
/ * *
* @param token
* @param scope
* @param productTitle
* @param productUrl
* @param enableNotification
* @param hashtags
* @param userFullName
* /
public WritePostCatalogueManagerThread (
String username , String scope ,
String productTitle , String productUrl , boolean enableNotification ,
2018-02-09 15:40:21 +01:00
List < String > hashtags , String userFullName , String userCurrentUrl ) {
2016-10-28 18:47:40 +02:00
super ( ) ;
this . username = username ;
this . scope = scope ;
this . productTitle = productTitle ;
this . productUrl = productUrl ;
this . enableNotification = enableNotification ;
this . hashtags = hashtags ;
this . userFullName = userFullName ;
2018-02-09 15:40:21 +01:00
this . userCurrentUrl = userCurrentUrl ;
2016-10-28 18:47:40 +02:00
}
@Override
public void run ( ) {
try {
// evaluate user's token for this scope
2017-04-25 15:41:27 +02:00
String token = GenericUtils . tryGetElseCreateToken ( username , scope ) ;
2017-02-28 12:36:29 +01:00
2016-11-17 14:08:22 +01:00
if ( token = = null ) {
logger . warn ( " Unable to proceed, user's token is not available " ) ;
return ;
}
2016-10-28 18:47:40 +02:00
logger . info ( " Started request to write application post "
+ " for new product created. Scope is " + scope + " and "
+ " token is " + token . substring ( 0 , 10 ) + " **************** " ) ;
// set token and scope
ScopeProvider . instance . set ( scope ) ;
SecurityTokenProvider . instance . set ( token ) ;
2018-02-09 15:40:21 +01:00
final String profilePageURL = GCubePortalConstants . PREFIX_GROUP_URL + extractOrgFriendlyURL ( userCurrentUrl ) + GCubePortalConstants . USER_PROFILE_FRIENDLY_URL ;
userFullName = " <a class= \" link \" href= \" " + profilePageURL + " ? " +
Base64 . getEncoder ( ) . encodeToString ( GCubeSocialNetworking . USER_PROFILE_OID . getBytes ( ) ) + " = " +
Base64 . getEncoder ( ) . encodeToString ( username . getBytes ( ) ) + " \" > " + userFullName +
" </a> " ;
2016-10-28 18:47:40 +02:00
// write
2017-02-28 12:36:29 +01:00
writeProductPost (
2016-10-28 18:47:40 +02:00
productTitle ,
productUrl ,
userFullName ,
hashtags ,
enableNotification
) ;
} catch ( Exception e ) {
logger . error ( " Failed to write the post because of the following error " , e ) ;
} finally {
SecurityTokenProvider . instance . reset ( ) ;
ScopeProvider . instance . reset ( ) ;
}
}
2017-02-28 12:36:29 +01:00
2018-02-09 15:40:21 +01:00
public static String extractOrgFriendlyURL ( String portalURL ) {
String groupRegEx = " /group/ " ;
if ( portalURL . contains ( groupRegEx ) ) {
String [ ] splits = portalURL . split ( groupRegEx ) ;
String friendlyURL = splits [ 1 ] ;
if ( friendlyURL . contains ( " / " ) ) {
friendlyURL = friendlyURL . split ( " / " ) [ 0 ] ;
} else {
friendlyURL = friendlyURL . split ( " \\ ? " ) [ 0 ] . split ( " \\ # " ) [ 0 ] ;
}
return " / " + friendlyURL ;
}
return null ;
}
2017-02-28 12:36:29 +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
* /
private 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 GCoreEndPointReaderSocial ( 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 {
2018-02-09 15:40:21 +01:00
JSONObject request = new JSONObject ( ) ;
request . put ( " app_id " , APPLICATION_ID_CATALOGUE_MANAGER ) ;
HttpResponse response = performRequest ( client , path , request . toJSONString ( ) ) ;
2017-02-28 12:36:29 +01:00
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 < String > 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
}
2018-02-09 15:40:21 +01:00
JSONObject request = new JSONObject ( ) ;
request . put ( " text " , message ) ;
request . put ( " enable_notification " , enablePostNotification ) ;
logger . info ( " The post that is going to be written is -> \ n " + request . toJSONString ( ) ) ;
HttpResponse response = performRequest ( client , path , request . toJSONString ( ) ) ;
2017-02-28 12:36:29 +01:00
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
2018-02-09 15:40:21 +01:00
throw new RuntimeException ( " Failed to write the post " ) ;
2017-02-28 12:36:29 +01:00
} catch ( Exception e ) {
2018-02-09 15:40:21 +01:00
logger . error ( " Failed to write the post " , e ) ;
2017-02-28 12:36:29 +01:00
}
}
2018-02-09 15:40:21 +01:00
2017-02-28 12:36:29 +01:00
/ * *
* 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 ) ;
}
2016-11-17 14:08:22 +01:00
}