diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..36540ab
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.project b/.project
new file mode 100644
index 0000000..07a493b
--- /dev/null
+++ b/.project
@@ -0,0 +1,42 @@
+
+
+ social-networking-library-ws
+
+
+
+
+
+ org.eclipse.wst.jsdt.core.javascriptValidator
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.wst.common.project.facet.core.builder
+
+
+
+
+ org.eclipse.wst.validation.validationbuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jem.workbench.JavaEMFNature
+ org.eclipse.wst.common.modulecore.ModuleCoreNature
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.wst.common.project.facet.core.nature
+ org.eclipse.wst.jsdt.core.jsNature
+
+
diff --git a/.settings/.jsdtscope b/.settings/.jsdtscope
new file mode 100644
index 0000000..b72a6a4
--- /dev/null
+++ b/.settings/.jsdtscope
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..839d647
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,5 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding//src/test/java=UTF-8
+encoding/=UTF-8
diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..443e085
--- /dev/null
+++ b/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,8 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs
new file mode 100644
index 0000000..f897a7f
--- /dev/null
+++ b/.settings/org.eclipse.m2e.core.prefs
@@ -0,0 +1,4 @@
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=true
+version=1
diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component
new file mode 100644
index 0000000..99ec679
--- /dev/null
+++ b/.settings/org.eclipse.wst.common.component
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+ uses
+
+
+ uses
+
+
+ uses
+
+
+ uses
+
+
+ uses
+
+
+
+
+
diff --git a/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml b/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml
new file mode 100644
index 0000000..cc81385
--- /dev/null
+++ b/.settings/org.eclipse.wst.common.project.facet.core.prefs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml
new file mode 100644
index 0000000..af37384
--- /dev/null
+++ b/.settings/org.eclipse.wst.common.project.facet.core.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/.settings/org.eclipse.wst.jsdt.ui.superType.container b/.settings/org.eclipse.wst.jsdt.ui.superType.container
new file mode 100644
index 0000000..3bd5d0a
--- /dev/null
+++ b/.settings/org.eclipse.wst.jsdt.ui.superType.container
@@ -0,0 +1 @@
+org.eclipse.wst.jsdt.launching.baseBrowserLibrary
\ No newline at end of file
diff --git a/.settings/org.eclipse.wst.jsdt.ui.superType.name b/.settings/org.eclipse.wst.jsdt.ui.superType.name
new file mode 100644
index 0000000..05bd71b
--- /dev/null
+++ b/.settings/org.eclipse.wst.jsdt.ui.superType.name
@@ -0,0 +1 @@
+Window
\ No newline at end of file
diff --git a/.settings/org.eclipse.wst.validation.prefs b/.settings/org.eclipse.wst.validation.prefs
new file mode 100644
index 0000000..04cad8c
--- /dev/null
+++ b/.settings/org.eclipse.wst.validation.prefs
@@ -0,0 +1,2 @@
+disabled=06target
+eclipse.preferences.version=1
diff --git a/distro/LICENSE b/distro/LICENSE
new file mode 100644
index 0000000..2d9616a
--- /dev/null
+++ b/distro/LICENSE
@@ -0,0 +1 @@
+${gcube.license}
\ No newline at end of file
diff --git a/distro/README b/distro/README
new file mode 100644
index 0000000..fc6be41
--- /dev/null
+++ b/distro/README
@@ -0,0 +1,62 @@
+The gCube System - ${name}
+--------------------------------------------------
+
+${description}
+
+
+${gcube.description}
+
+${gcube.funding}
+
+
+Version
+--------------------------------------------------
+
+${version} (${buildDate})
+
+Please see the file named "changelog.xml" in this directory for the release notes.
+
+
+Authors
+--------------------------------------------------
+
+* Costantino Perciante (costantino.perciante@isti.cnr.it), Istituto di Scienza e Tecnologie dell'Informazione "A. Faedo" - CNR, Pisa (Italy).
+
+Maintainers
+-----------
+
+* Costantino Perciante (costantino.perciante@isti.cnr.it), Istituto di Scienza e Tecnologie dell'Informazione "A. Faedo" - CNR, Pisa (Italy).
+
+Download information
+--------------------------------------------------
+
+Source code is available from SVN:
+ ${scm.url}
+
+Binaries can be downloaded from the gCube website:
+ ${gcube.website}
+
+
+Installation
+--------------------------------------------------
+
+Installation documentation is available on-line in the gCube Wiki:
+ ${gcube.wikiRoot}
+
+Documentation
+--------------------------------------------------
+
+Documentation is available on-line in the gCube Wiki:
+ ${gcube.wikiRoot}
+
+Support
+--------------------------------------------------
+
+Bugs and support requests can be reported in the gCube issue tracking tool:
+ ${gcube.issueTracking}
+
+
+Licensing
+--------------------------------------------------
+
+This software is licensed under the terms you may find in the file named "LICENSE" in this directory.
\ No newline at end of file
diff --git a/distro/changelog.xml b/distro/changelog.xml
new file mode 100644
index 0000000..222c1f5
--- /dev/null
+++ b/distro/changelog.xml
@@ -0,0 +1,63 @@
+
+
+ added get-oauth-profile method for returning Google similar
+ JSON Profile
+
+
+ Fix for #10997
+
+
+ Support for tickets #9333, #9332, #9276
+ Added support for caches (ehcache)
+
+
+ Fixed bug #8685
+
+
+ Moved as service on top of SmartGears
+ Exploiting Liferay's Web APIs
+
+
+ Custom jackson object mapper is used to have snake case
+ fields'names
+
+ Improved users version 2 resource with more methods
+
+
+ Moved to new Authorization Framework
+ Added method to send Messages
+ Added new version for the APIs that accept/return JSON
+
+ Token is discovered before every call is executed by a filter
+
+
+
+ Support for new portal (gCube 4.0.0)
+
+
+ Minor bug fixes (look at support ticket #2971)
+
+
+ First Release
+
+
diff --git a/distro/descriptor.xml b/distro/descriptor.xml
new file mode 100644
index 0000000..b3f5628
--- /dev/null
+++ b/distro/descriptor.xml
@@ -0,0 +1,31 @@
+
+ servicearchive
+
+ tar.gz
+
+ /
+
+
+ ${distroDirectory}
+ /
+ true
+
+ README
+ LICENSE
+ changelog.xml
+ profile.xml
+
+ 755
+ true
+
+
+
+
+
+ /${artifactId}
+
+
+
\ No newline at end of file
diff --git a/distro/profile.xml b/distro/profile.xml
new file mode 100644
index 0000000..4dda0b9
--- /dev/null
+++ b/distro/profile.xml
@@ -0,0 +1,25 @@
+
+
+
+ Library
+
+ ${description}
+ Portal
+ ${artifactId}
+ 1.0.0
+
+
+ ${artifactId}
+ ${version}
+
+ ${groupId}
+ ${artifactId}
+ ${version}
+
+
+ ${build.finalName}.war
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..999ba2e
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,395 @@
+
+
+ 4.0.0
+
+ maven-parent
+ org.gcube.tools
+ 1.0.0
+
+
+
+ org.gcube.portal
+ social-networking-library-ws
+ war
+ 2.2.3-SNAPSHOT
+ social-networking-library-ws
+ Rest interface for the social networking library.
+
+ 1.8
+ 1.5.13
+ 2.25.1
+ ${project.basedir}/distro
+ ${project.build.directory}/${project.build.finalName}
+ distro
+ UTF-8
+ UTF-8
+
+
+
+ scm:svn:http://svn.d4science.research-infrastructures.eu/gcube/trunk/portal/${project.artifactId}
+ scm:https://svn.d4science.research-infrastructures.eu/gcube/trunk/portal/${project.artifactId}
+ http://svn.d4science.research-infrastructures.eu/gcube/trunk/portal/${project.artifactId}
+
+
+
+
+
+ org.gcube.distribution
+ maven-smartgears-bom
+ LATEST
+ pom
+ import
+
+
+
+
+
+
+ net.sf.ehcache
+ ehcache
+ 2.10.0
+
+
+ org.gcube.common.portal
+ portal-manager
+ [2.0.0-SNAPSHOT,)
+ compile
+
+
+ com.sun.mail
+ javax.mail
+ 1.5.2
+ compile
+
+
+ commons-lang
+ commons-lang
+ 2.6
+ compile
+
+
+ com.liferay.portal
+ portal-service
+ 6.2.5
+ compile
+
+
+ com.google.guava
+ guava
+ compile
+ 18.0
+
+
+ org.slf4j
+ slf4j-api
+ provided
+
+
+ com.netflix.astyanax
+ astyanax
+ 2.0.2
+ compile
+
+
+ jersey-client
+ com.sun.jersey
+
+
+ jersey-bundle
+ com.sun.jersey
+
+
+ jersey-apache-client4
+ com.sun.jersey.contribs
+
+
+
+
+
+
+
+
+ com.netflix.astyanax
+ astyanax-thrift
+ 2.0.2
+ compile
+
+
+ com.netflix.astyanax
+ astyanax-cassandra
+ 2.0.2
+ compile
+
+
+ com.netflix.astyanax
+ astyanax-core
+ 2.0.2
+ compile
+
+
+ org.slf4j
+ log4j-over-slf4j
+
+
+
+
+ org.gcube.socialnetworking
+ social-util-library
+ [1.0.1-SNAPSHOT,2.0.0-SNAPSHOT)
+ compile
+
+
+ org.gcube.portal
+ social-networking-library
+ [1.0.0-SNAPSHOT,)
+ compile
+
+
+ org.gcube.dvos
+ usermanagement-core
+ [2.0.0-SNAPSHOT,)
+ compile
+
+
+ org.gcube.resources.discovery
+ ic-client
+ provided
+
+
+ org.gcube.core
+ common-encryption
+ provided
+
+
+ org.gcube.common
+ authorization-client
+ provided
+
+
+ org.gcube.common
+ home-library
+ [2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT)
+ compile
+
+
+ org.gcube.common
+ home-library-jcr
+ [2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT)
+ compile
+
+
+ junit
+ junit
+ 4.8
+ test
+
+
+ org.gcube.socialnetworking
+ social-data-search-client
+ [1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)
+ compile
+
+
+
+
+
+
+
+
+
+
+
+
+
+ jackson-dataformat-yaml
+ com.fasterxml.jackson.dataformat
+ 2.8.6
+
+
+
+ jackson-dataformat-smile
+ com.fasterxml.jackson.dataformat
+ 2.8.6
+
+
+
+ jackson-dataformat-cbor
+ com.fasterxml.jackson.dataformat
+ 2.8.6
+
+
+
+
+ com.ning
+ compress-lzf
+ 1.0.3
+ compile
+
+
+ org.glassfish.jersey.containers
+
+ jersey-container-servlet-core
+ ${version.jersey}
+
+
+ org.glassfish.jersey.media
+ jersey-media-json-jackson
+ ${version.jersey}
+
+
+ org.glassfish.jersey.media
+ jersey-media-json-processing
+ ${version.jersey}
+
+
+ org.glassfish.jersey.media
+ jersey-media-multipart
+ ${version.jersey}
+
+
+ org.glassfish.jersey.media
+ jersey-media-sse
+ ${version.jersey}
+
+
+ org.glassfish.jersey.ext
+ jersey-bean-validation
+ ${version.jersey}
+
+
+
+
+
+
+
+
+
+ io.swagger
+ swagger-jersey2-jaxrs
+ ${version.swagger}
+ provided
+
+
+ jackson-dataformat-yaml
+ com.fasterxml.jackson.dataformat
+
+
+
+
+ javax.portlet
+ portlet-api
+ [2.0.0-SNAPSHOT,)
+ compile
+
+
+
+ org.gcube.core
+ common-smartgears
+ provided
+
+
+ org.gcube.core
+ common-smartgears-app
+ compile
+
+
+
+
+ org.gcube.portal
+ notifications-common-library
+ [1.3.0-SNAPSHOT,)
+ compile
+
+
+ org.gcube.applicationsupportlayer
+ aslsocial
+ [1.0.0-SNAPSHOT,2.0.0-SNAPSHOT)
+ compile
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.3
+ compile
+
+
+
+
+ ${name}
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ 2.1.1
+
+
+ compile
+
+ exploded
+
+
+
+
+ ${webappDirectory}
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.3.2
+
+
+ 1.7
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 2.2
+
+
+ ${distroDirectory}/descriptor.xml
+
+
+
+
+ servicearchive
+ install
+
+ single
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 2.5
+
+
+ copy-profile
+ install
+
+ copy-resources
+
+
+ target
+
+
+ ${distroDirectory}
+ true
+
+ profile.xml
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/gcube/portal/social/networking/caches/CachesManager.java b/src/main/java/org/gcube/portal/social/networking/caches/CachesManager.java
new file mode 100644
index 0000000..1df6a4c
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/caches/CachesManager.java
@@ -0,0 +1,34 @@
+package org.gcube.portal.social.networking.caches;
+
+import net.sf.ehcache.CacheManager;
+import net.sf.ehcache.Ehcache;
+
+/**
+ * Handle caches via Ehcache
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class CachesManager {
+
+ private static CacheManager cacheManager;
+ public static final CachesManager singleton = new CachesManager();
+
+ // the following caches are declared within the ehcache.xml (no default is available)
+ public static final String SOCIAL_NETWORKING_SITES_CACHE = "social_networking_site_cache";
+ public static final String USERS_CACHE = "users_cache";
+ public static final String GROUPS_CACHE = "groups_cache";
+
+ private CachesManager(){
+ cacheManager = CacheManager.newInstance();
+ }
+
+ public static Ehcache getCache(String name){
+ return cacheManager.getEhcache(name);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ cacheManager.shutdown();
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/caches/GroupsCache.java b/src/main/java/org/gcube/portal/social/networking/caches/GroupsCache.java
new file mode 100644
index 0000000..407ca02
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/caches/GroupsCache.java
@@ -0,0 +1,54 @@
+package org.gcube.portal.social.networking.caches;
+
+import net.sf.ehcache.Ehcache;
+import net.sf.ehcache.Element;
+
+import org.gcube.vomanagement.usermanagement.model.GCubeGroup;
+import org.slf4j.LoggerFactory;
+
+public class GroupsCache {
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(GroupsCache.class);
+ private static GroupsCache singleton = new GroupsCache();
+
+ /**
+ * Private constructor: build the cache
+ * @return
+ */
+ private GroupsCache(){
+
+ logger.info("Building cache");
+ CachesManager.getCache(CachesManager.GROUPS_CACHE);
+
+ }
+
+ /**
+ * Get the singleton object
+ */
+ public static GroupsCache getSingleton() {
+ return singleton;
+ }
+
+ /**
+ * Retrieve an entry
+ * @param id
+ * @return user associated to the user
+ */
+ public GCubeGroup getGroup(long groupId){
+ Ehcache groupsCache = CachesManager.getCache(CachesManager.GROUPS_CACHE);
+ if(groupsCache.get(groupId) != null)
+ return (GCubeGroup) groupsCache.get(groupId).getObjectValue();
+ else
+ return null;
+ }
+
+ /**
+ * Save an entry into the cache
+ * @param id
+ * @param user
+ */
+ public void pushEntry(long id, GCubeGroup group){
+ Ehcache groupsCache = CachesManager.getCache(CachesManager.GROUPS_CACHE);
+ groupsCache.put(new Element(id, group));
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/caches/SocialNetworkingSiteFinder.java b/src/main/java/org/gcube/portal/social/networking/caches/SocialNetworkingSiteFinder.java
new file mode 100644
index 0000000..cfe1928
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/caches/SocialNetworkingSiteFinder.java
@@ -0,0 +1,248 @@
+package org.gcube.portal.social.networking.caches;
+
+import static org.gcube.resources.discovery.icclient.ICFactory.clientFor;
+import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
+
+import java.io.InputStream;
+import java.util.List;
+import java.util.Properties;
+
+import net.sf.ehcache.Ehcache;
+import net.sf.ehcache.Element;
+
+import org.gcube.applicationsupportlayer.social.shared.SocialNetworkingSite;
+import org.gcube.common.portal.GCubePortalConstants;
+import org.gcube.common.resources.gcore.ServiceEndpoint;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.social.networking.liferay.ws.GroupManagerWSBuilder;
+import org.gcube.resources.discovery.client.api.DiscoveryClient;
+import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
+import org.gcube.smartgears.ContextProvider;
+import org.gcube.smartgears.context.application.ApplicationContext;
+import org.gcube.vomanagement.usermanagement.GroupManager;
+import org.gcube.vomanagement.usermanagement.model.GCubeGroup;
+import org.gcube.vomanagement.usermanagement.model.VirtualGroup;
+import org.slf4j.LoggerFactory;
+
+/**
+ * When a notification needs to be sent, this class offers utility to discover (starting from the scope)
+ * the site information needed to build up the SocialNetworkingSite object (which, for instance, contains the
+ * portal email).
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class SocialNetworkingSiteFinder {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SocialNetworkingSiteFinder.class);
+
+ private static final String EMAIL_SENDER_SITE_CUSTOM_FIELD = "Emailsender";
+ private static final String CATEGORY = "Portal";
+
+ // these properties could be overwritten by the ones read from config.properties
+ private static String PROD_FALLBACK_GATEWAY = "D4Science.org Gateway";
+ private static String DEV_FALLBACK_GATEWAY = "gCube Dev4 Snapshot Gateway";
+ private static String PREPROD_FALLBACK_GATEWAY = "gCube Preprod (dev) Gateway";
+
+ /**
+ * Singleton object
+ */
+ private static SocialNetworkingSiteFinder singleton = new SocialNetworkingSiteFinder();
+
+ /**
+ * Build the singleton instance
+ */
+ private SocialNetworkingSiteFinder(){
+
+ // read fallback properties
+ try{
+ logger.info("Trying to read config.properties");
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ InputStream input = classLoader.getResourceAsStream("config.properties");
+ Properties properties = new Properties();
+ properties.load(input);
+ PROD_FALLBACK_GATEWAY = properties.getProperty("PROD_FALLBACK_GATEWAY");
+ DEV_FALLBACK_GATEWAY = properties.getProperty("DEV_FALLBACK_GATEWAY");
+ PREPROD_FALLBACK_GATEWAY = properties.getProperty("PREPROD_FALLBACK_GATEWAY");
+ }catch(Exception e){
+ logger.warn("Failed to read config.properties...", e);
+ }
+
+ }
+
+ /**
+ * Retrieve the singleton instance
+ */
+ public static SocialNetworkingSiteFinder getInstance(){
+
+ return singleton;
+
+ }
+
+ /**
+ * Retrieve the SocialNetworkingSite given the scope
+ * @param scope
+ * @return
+ */
+ public static SocialNetworkingSite getSocialNetworkingSiteFromScope(String scope){
+
+ Ehcache socialSitesCache = CachesManager.getCache(CachesManager.SOCIAL_NETWORKING_SITES_CACHE);
+
+ if(scope == null || scope.isEmpty())
+ throw new IllegalArgumentException("Scope cannot be null/empty");
+
+ if(socialSitesCache.get(scope) != null)
+ return (SocialNetworkingSite) socialSitesCache.get(scope).getObjectValue();
+ else{
+ SocialNetworkingSite site = discoverSite(scope);
+ if(site != null)
+ socialSitesCache.put(new Element(scope, site));
+ return site;
+ }
+
+ }
+
+ /**
+ * Discover the site for this scope
+ * @param scope
+ * @return
+ */
+ private static SocialNetworkingSite discoverSite(String scope) {
+
+ try{
+
+ logger.info("Requested site for scope " + scope);
+ GroupManager groupManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+ long groupId = groupManager.getGroupIdFromInfrastructureScope(scope);
+
+ GCubeGroup matchingGateway = null;
+
+ if(groupManager.isVRE(groupId)){
+
+ // get the Virtual groups for the groupid related to the scope
+
+ List virtualGroupsOfGroup = groupManager.getVirtualGroups(groupId);
+
+ if(virtualGroupsOfGroup == null || virtualGroupsOfGroup.isEmpty())
+ throw new Exception("It seems that the VRE is not linked to any VirtualGroups");
+
+ // get the gateways
+ List gateways = groupManager.getGateways();
+
+ if(gateways == null || gateways.isEmpty())
+ throw new Exception("It seems there is no gateway here!");
+
+ logger.info("Retrieved Gateways are " + gateways);
+
+ // now, retrieve the virtual groups for each gateway and stop when a VG matches with one of the group
+ // then, it is the gateway of interest
+ ext_loop: for (GCubeGroup gateway : gateways) {
+ List gatewayVirtualGroups = groupManager.getVirtualGroups(gateway.getGroupId());
+ if(gatewayVirtualGroups != null && !gatewayVirtualGroups.isEmpty()){
+ for (VirtualGroup gatewayVirtualGroup : gatewayVirtualGroups) {
+ if(virtualGroupsOfGroup.contains(gatewayVirtualGroup)){
+ logger.info("Matching gateway for scope " + scope + " is " + gateway);
+ matchingGateway = gateway;
+ break ext_loop;
+ }
+ }
+ }
+ }
+
+ }else{
+
+ List gateways = groupManager.getGateways();
+
+ // vo and root vo cases are treated separately: in production environment services.d4science.org is used, instead
+ // in dev next.d4science.org is used TODO better way...
+ ApplicationContext ctx = ContextProvider.get(); // get this info from SmartGears
+ String rootContext = "/"+ctx.container().configuration().infrastructure();
+ String matchingGatewayName = null;
+ if(isDevOrPreprod(rootContext)){
+ matchingGatewayName = DEV_FALLBACK_GATEWAY;
+ }else{
+ matchingGatewayName = PROD_FALLBACK_GATEWAY;
+ }
+
+ // find the matching one among the gateways
+ for (GCubeGroup gateway : gateways) {
+ if(gateway.getGroupName().equals(matchingGatewayName)){
+ matchingGateway = gateway;
+ break;
+ }
+ }
+
+ if(matchingGateway == null && isDevOrPreprod(rootContext)){
+
+ logger.warn("Checking if it is the preprod environment");
+ matchingGatewayName = PREPROD_FALLBACK_GATEWAY;
+ // find the matching one among the gateways
+ for (GCubeGroup gateway : gateways) {
+ if(gateway.getGroupName().equals(matchingGatewayName)){
+ matchingGateway = gateway;
+ break;
+ }
+ }
+
+ }
+
+ }
+
+ if(matchingGateway == null){
+ logger.warn("There is no gateway for such scope. Returning null");
+ return null;
+ }else{
+ String siteName = matchingGateway.getGroupName();
+ String emailSender = (String)groupManager.readCustomAttr(matchingGateway.getGroupId(), EMAIL_SENDER_SITE_CUSTOM_FIELD);
+ String siteLandingPagePath = GCubePortalConstants.PREFIX_GROUP_URL + matchingGateway.getFriendlyURL();
+ String siteUrl = discoverHostOfServiceEndpoint(siteName);
+ SocialNetworkingSite site = new SocialNetworkingSite(siteName, emailSender, siteUrl, siteLandingPagePath);
+ logger.info("Site is " + site);
+ return site;
+ }
+
+ }catch(Exception e){
+ logger.error("Failed to determine the SocialNetworkingSite for scope " + scope, e);
+ }
+
+ return null;
+ }
+
+ private static boolean isDevOrPreprod(String rootContext) {
+ return rootContext.equals("/gcube");
+ }
+
+ /**
+ * Retrieve endpoint host from IS for this gateway
+ * @return the host for the gateway
+ * @throws Exception
+ */
+ private static String discoverHostOfServiceEndpoint(String gatewayName){
+
+ String currentScope = ScopeProvider.instance.get();
+ ApplicationContext ctx = ContextProvider.get(); // get this info from SmartGears
+ ScopeProvider.instance.set("/"+ctx.container().configuration().infrastructure());
+ String host = null;
+ try{
+
+ SimpleQuery query = queryFor(ServiceEndpoint.class);
+ query.addCondition("$resource/Profile/Name/text() eq '"+ gatewayName +"'");
+ query.addCondition("$resource/Profile/Category/text() eq '"+ CATEGORY +"'");
+ DiscoveryClient client = clientFor(ServiceEndpoint.class);
+ List toReturn = client.submit(query);
+ for (ServiceEndpoint serviceEndpoint : toReturn) {
+ host = "https://" + serviceEndpoint.profile().runtime().hostedOn();
+ logger.info("Gateway host is " + host);
+ break;
+ }
+
+ }catch(Exception e){
+ logger.error("Error while retrieving host for the gateway " + gatewayName);
+ }finally{
+ ScopeProvider.instance.set(currentScope);
+ }
+
+ return host;
+
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/caches/UsersCache.java b/src/main/java/org/gcube/portal/social/networking/caches/UsersCache.java
new file mode 100644
index 0000000..c399965
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/caches/UsersCache.java
@@ -0,0 +1,83 @@
+package org.gcube.portal.social.networking.caches;
+
+import java.util.List;
+
+import net.sf.ehcache.Ehcache;
+import net.sf.ehcache.Element;
+
+import org.gcube.portal.social.networking.liferay.ws.GroupManagerWSBuilder;
+import org.gcube.portal.social.networking.liferay.ws.UserManagerWSBuilder;
+import org.gcube.smartgears.ContextProvider;
+import org.gcube.smartgears.context.application.ApplicationContext;
+import org.gcube.vomanagement.usermanagement.GroupManager;
+import org.gcube.vomanagement.usermanagement.UserManager;
+import org.gcube.vomanagement.usermanagement.model.GCubeUser;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This cache will store GCubeUser of the users of the infrastructure as couples {user-id, user screename}
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class UsersCache{
+
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(UsersCache.class);
+ private static UsersCache singleton = new UsersCache();
+
+ /**
+ * Private constructor: build the cache
+ * @return
+ */
+ private UsersCache(){
+
+ // create a thread to build the cache
+ new Thread(){
+ public void run() {
+ try{
+ logger.info("Fetching users and putting them into cache");
+ Ehcache usersCache = CachesManager.getCache(CachesManager.USERS_CACHE);
+ GroupManager groupManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ ApplicationContext ctx = ContextProvider.get(); // get this info from SmartGears
+ List users = userManager.listUsersByGroup(groupManager.getGroupIdFromInfrastructureScope("/"+ctx.container().configuration().infrastructure()));
+ for (GCubeUser gCubeUser : users) {
+ usersCache.put(new Element(gCubeUser.getUserId(), gCubeUser));
+ }
+ }catch(Exception e){
+ logger.error("Unable to retrieve user's usernames. Other users will be discovered later on", e);
+ }
+ }
+ }.start();
+
+ }
+
+ /**
+ * Get the singleton object
+ */
+ public static UsersCache getSingleton() {
+ return singleton;
+ }
+
+ /**
+ * Retrieve an entry
+ * @param id
+ * @return user associated to the user
+ */
+ public GCubeUser getUser(long userId){
+ Ehcache usersCache = CachesManager.getCache(CachesManager.USERS_CACHE);
+ if(usersCache.get(userId) != null)
+ return (GCubeUser) usersCache.get(userId).getObjectValue();
+ else
+ return null;
+ }
+
+ /**
+ * Save an entry into the cache
+ * @param id
+ * @param user
+ */
+ public void pushEntry(long id, GCubeUser user){
+ Ehcache usersCache = CachesManager.getCache(CachesManager.USERS_CACHE);
+ usersCache.put(new Element(id, user));
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/liferay/ws/GroupManagerWSBuilder.java b/src/main/java/org/gcube/portal/social/networking/liferay/ws/GroupManagerWSBuilder.java
new file mode 100644
index 0000000..a83b36e
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/liferay/ws/GroupManagerWSBuilder.java
@@ -0,0 +1,50 @@
+package org.gcube.portal.social.networking.liferay.ws;
+
+import org.gcube.vomanagement.usermanagement.GroupManager;
+import org.gcube.vomanagement.usermanagement.impl.ws.LiferayWSGroupManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class that builds a (singleton) GroupManagerWS object.
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class GroupManagerWSBuilder {
+
+ private static final Logger logger = LoggerFactory.getLogger(GroupManagerWSBuilder.class);
+ private static GroupManagerWSBuilder singleton = new GroupManagerWSBuilder();
+ private static GroupManager groupManagerWs;
+
+ private GroupManagerWSBuilder(){
+
+ logger.info("Building GroupManager please wait");
+
+ try{
+ groupManagerWs = new LiferayWSGroupManager(
+ LiferayJSONWsCredentials.getSingleton().getUser(),
+ LiferayJSONWsCredentials.getSingleton().getPassword(),
+ LiferayJSONWsCredentials.getSingleton().getHost(),
+ LiferayJSONWsCredentials.getSingleton().getSchema(),
+ LiferayJSONWsCredentials.getSingleton().getPort());
+ }catch(Exception e){
+ logger.error("Failed to build the GroupManager. ", e);
+ return;
+ }
+
+ logger.info("GroupManager instance built");
+
+ }
+
+ /**
+ * Get the user manager instance
+ * @return
+ */
+ public GroupManager getGroupManager(){
+ return groupManagerWs;
+ }
+
+ public static GroupManagerWSBuilder getInstance(){
+ return singleton;
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/liferay/ws/LiferayJSONWsCredentials.java b/src/main/java/org/gcube/portal/social/networking/liferay/ws/LiferayJSONWsCredentials.java
new file mode 100644
index 0000000..6543a30
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/liferay/ws/LiferayJSONWsCredentials.java
@@ -0,0 +1,175 @@
+package org.gcube.portal.social.networking.liferay.ws;
+
+import static org.gcube.resources.discovery.icclient.ICFactory.clientFor;
+import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.gcube.common.encryption.StringEncrypter;
+import org.gcube.common.resources.gcore.ServiceEndpoint;
+import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint;
+import org.gcube.common.resources.gcore.ServiceEndpoint.Property;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.resources.discovery.client.api.DiscoveryClient;
+import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
+import org.gcube.smartgears.ContextProvider;
+import org.gcube.smartgears.context.application.ApplicationContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+/**
+ * This is a singleton bean instantiated at service start up. It contains
+ * the credentials of the user who is allowed to perform calls to Liferay.
+ * Its credentials are looked up from the infrastructure.
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class LiferayJSONWsCredentials {
+
+ private static final Logger logger = LoggerFactory.getLogger(LiferayJSONWsCredentials.class);
+
+ // the singleton obj
+ private static LiferayJSONWsCredentials singleton = new LiferayJSONWsCredentials();
+
+ // properties that it contains
+ private String host;
+ private String schema;
+ private String user;
+ private String password;
+ private int port;
+
+ // The token of the user used to send notifications/messages when an application token is provided. (will be read from web.xml)
+ private String notifierUserToken;
+
+ // Service endpoint properties
+ private final static String RUNTIME_RESOURCE_NAME = "D4Science Infrastructure Gateway";
+ private final static String CATEGORY = "Portal";
+
+ /**
+ * Private constructor
+ */
+ private LiferayJSONWsCredentials() {
+ logger.info("Building LiferayJSONWsCredentials object");
+ loadNotifierToken();
+ lookupPropertiesFromIs();
+ logger.info("LiferayJSONWsCredentials object built");
+ }
+
+ /**
+ * Load the token of the notifier user
+ */
+ private void loadNotifierToken() {
+ try{
+ notifierUserToken = ServletContextClass.getNotifierToken();
+ logger.debug("Token read " + notifierUserToken.substring(0, 5)+ "*********************");
+ }catch(Exception e){
+ logger.error("Failed to read notifier user token!", e);
+ }
+ }
+
+ /**
+ * Read the properties from the infrastructure
+ */
+ private void lookupPropertiesFromIs() {
+
+ logger.info("Starting creating LiferayJSONWsCredentials");
+
+ String oldContext = ScopeProvider.instance.get();
+ ApplicationContext ctx = ContextProvider.get(); // get this info from SmartGears
+ ScopeProvider.instance.set("/"+ctx.container().configuration().infrastructure());
+ logger.info("Discovering liferay user's credentials in context " + ctx.container().configuration().infrastructure());
+
+ try{
+ List resources = getConfigurationFromIS();
+ if (resources.size() == 0){
+ logger.error("There is no Runtime Resource having name " + RUNTIME_RESOURCE_NAME +" and Category " + CATEGORY + " in this scope.");
+ throw new Exception("There is no Runtime Resource having name " + RUNTIME_RESOURCE_NAME +" and Category " + CATEGORY + " in this scope.");
+ }
+ else {
+ for (ServiceEndpoint res : resources) {
+ Iterator accessPointIterator = res.profile().accessPoints().iterator();
+ while (accessPointIterator.hasNext()) {
+ ServiceEndpoint.AccessPoint accessPoint = (ServiceEndpoint.AccessPoint) accessPointIterator
+ .next();
+
+ if(accessPoint.name().equals("JSONWSUser")){
+ // get base path
+ Map properties = accessPoint.propertyMap();
+ host = accessPoint.address();
+ schema = (String)properties.get("schema").value();
+ user = StringEncrypter.getEncrypter().decrypt((String)properties.get("username").value());
+ password = StringEncrypter.getEncrypter().decrypt((String)properties.get("password").value());
+ port = Integer.valueOf(properties.get("port").value());
+
+ // break
+ break;
+ }
+ }
+ }
+ }
+ }catch(Exception e){
+ logger.error("Unable to retrieve such service endpoint information!", e);
+ return;
+ }finally{
+ if(oldContext != null)
+ ScopeProvider.instance.set(oldContext);
+ }
+
+ logger.info("Bean built " + toString());
+ }
+
+ /**
+ * Retrieve endpoints information from IS for DB
+ * @return list of endpoints for ckan database
+ * @throws Exception
+ */
+ private List getConfigurationFromIS() throws Exception{
+
+ SimpleQuery query = queryFor(ServiceEndpoint.class);
+ query.addCondition("$resource/Profile/Name/text() eq '"+ RUNTIME_RESOURCE_NAME +"'");
+ query.addCondition("$resource/Profile/Category/text() eq '"+ CATEGORY +"'");
+ DiscoveryClient client = clientFor(ServiceEndpoint.class);
+ List toReturn = client.submit(query);
+ return toReturn;
+
+ }
+
+ public static LiferayJSONWsCredentials getSingleton() {
+ return singleton;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public String getSchema() {
+ return schema;
+ }
+
+ public String getUser() {
+ return user;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getNotifierUserToken() {
+ return notifierUserToken;
+ }
+
+ @Override
+ public String toString() {
+ return "LiferayJSONWsCredentials [host=" + host + ", schema=" + schema
+ + ", user=" + user + ", password=" + password + ", port="
+ + port + ", notifierUserToken=" + notifierUserToken + "]";
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/liferay/ws/RoleManagerWSBuilder.java b/src/main/java/org/gcube/portal/social/networking/liferay/ws/RoleManagerWSBuilder.java
new file mode 100644
index 0000000..b5b230e
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/liferay/ws/RoleManagerWSBuilder.java
@@ -0,0 +1,50 @@
+package org.gcube.portal.social.networking.liferay.ws;
+
+import org.gcube.vomanagement.usermanagement.RoleManager;
+import org.gcube.vomanagement.usermanagement.impl.ws.LiferayWSRoleManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class that builds a (singleton) UserManagerWS object.
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class RoleManagerWSBuilder {
+
+ private static final Logger logger = LoggerFactory.getLogger(UserManagerWSBuilder.class);
+ private static RoleManagerWSBuilder singleton = new RoleManagerWSBuilder();
+ private RoleManager roleManagerWs;
+
+ private RoleManagerWSBuilder(){
+
+ logger.info("Building UserManager please wait");
+
+ try{
+ roleManagerWs = new LiferayWSRoleManager(
+ LiferayJSONWsCredentials.getSingleton().getUser(),
+ LiferayJSONWsCredentials.getSingleton().getPassword(),
+ LiferayJSONWsCredentials.getSingleton().getHost(),
+ LiferayJSONWsCredentials.getSingleton().getSchema(),
+ LiferayJSONWsCredentials.getSingleton().getPort());
+ }catch(Exception e){
+ logger.error("Failed to build the UserManager. ", e);
+ return;
+ }
+
+ logger.info("UserManager instance built");
+
+ }
+
+ /**
+ * Get the role manager instance
+ * @return
+ */
+ public RoleManager getRoleManager(){
+ return roleManagerWs;
+ }
+
+ public static RoleManagerWSBuilder getInstance(){
+ return singleton;
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/liferay/ws/ServletContextClass.java b/src/main/java/org/gcube/portal/social/networking/liferay/ws/ServletContextClass.java
new file mode 100644
index 0000000..5dcca04
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/liferay/ws/ServletContextClass.java
@@ -0,0 +1,39 @@
+package org.gcube.portal.social.networking.liferay.ws;
+
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.gcube.portal.social.networking.caches.UsersCache;
+
+/**
+ * Loaded at start up. This class performs some init - to be done once - operations.
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class ServletContextClass implements ServletContextListener
+{
+
+ private static String notifierToken;
+
+ public void contextInitialized(ServletContextEvent arg0) {
+
+ // get the token and save it
+ notifierToken = arg0.getServletContext().getInitParameter("NOTIFIER_TOKEN");
+
+ // start the thread to retrieve infrastructure users (which is, build up the singleton)
+ UsersCache.getSingleton();
+
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent arg0){
+ // on shutdown
+ }
+
+ /**
+ * Returns the token of the Liferay's User.
+ * @return
+ */
+ public static String getNotifierToken() {
+ return notifierToken;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/gcube/portal/social/networking/liferay/ws/UserManagerWSBuilder.java b/src/main/java/org/gcube/portal/social/networking/liferay/ws/UserManagerWSBuilder.java
new file mode 100644
index 0000000..d6016b2
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/liferay/ws/UserManagerWSBuilder.java
@@ -0,0 +1,50 @@
+package org.gcube.portal.social.networking.liferay.ws;
+
+import org.gcube.vomanagement.usermanagement.UserManager;
+import org.gcube.vomanagement.usermanagement.impl.ws.LiferayWSUserManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class that builds a (singleton) UserManagerWS object.
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class UserManagerWSBuilder {
+
+ private static final Logger logger = LoggerFactory.getLogger(UserManagerWSBuilder.class);
+ private static UserManagerWSBuilder singleton = new UserManagerWSBuilder();
+ private UserManager userManagerWs;
+
+ private UserManagerWSBuilder(){
+
+ logger.info("Building UserManager please wait");
+
+ try{
+ userManagerWs = new LiferayWSUserManager(
+ LiferayJSONWsCredentials.getSingleton().getUser(),
+ LiferayJSONWsCredentials.getSingleton().getPassword(),
+ LiferayJSONWsCredentials.getSingleton().getHost(),
+ LiferayJSONWsCredentials.getSingleton().getSchema(),
+ LiferayJSONWsCredentials.getSingleton().getPort());
+ }catch(Exception e){
+ logger.error("Failed to build the UserManager. ", e);
+ return;
+ }
+
+ logger.info("UserManager instance built");
+
+ }
+
+ /**
+ * Get the user manager instance
+ * @return
+ */
+ public UserManager getUserManager(){
+ return userManagerWs;
+ }
+
+ public static UserManagerWSBuilder getInstance(){
+ return singleton;
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/swagger/config/Bootstrap.java b/src/main/java/org/gcube/portal/social/networking/swagger/config/Bootstrap.java
new file mode 100644
index 0000000..d33e196
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/swagger/config/Bootstrap.java
@@ -0,0 +1,31 @@
+package org.gcube.portal.social.networking.swagger.config;
+
+import io.swagger.jaxrs.config.BeanConfig;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+
+/**
+ * Configuration servlet for swagger.
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@SuppressWarnings("serial")
+public class Bootstrap extends HttpServlet{
+
+ public static final String GCUBE_TOKEN_IN_QUERY_DEF = "gcube-token-query";
+ public static final String GCUBE_TOKEN_IN_HEADER_DEF = "gcube-token-header";
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+
+ super.init(config);
+ BeanConfig beanConfig = new BeanConfig();
+ beanConfig.setResourcePackage("org.gcube.portal.social.networking.ws");
+ beanConfig.setPrettyPrint(true); // print pretty json
+ beanConfig.setHost("socialnetworking1.d4science.org");
+ beanConfig.setBasePath("social-networking-library-ws/rest");
+ beanConfig.setScan(true);
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/gcube/portal/social/networking/swagger/config/SwaggerConstants.java b/src/main/java/org/gcube/portal/social/networking/swagger/config/SwaggerConstants.java
new file mode 100644
index 0000000..63fc6b6
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/swagger/config/SwaggerConstants.java
@@ -0,0 +1,25 @@
+package org.gcube.portal.social.networking.swagger.config;
+
+/**
+ * Swagger constants
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class SwaggerConstants {
+
+ public static final String SECURITY_SCHEMA = "gcube-token";
+ public static final String HTTP_SCHEMA = "http";
+ public static final String HTTPS_SCHEMA = "https";
+
+ // VALUES
+ public static final String USERS = "users";
+ public static final String TOKENS = "tokens";
+ public static final String POSTS = "posts";
+ public static final String PEOPLE = "people";
+ public static final String NOTIFICATIONS = "notifications";
+ public static final String MESSAGES = "messages";
+ public static final String HASHTAGS = "hashtags";
+ public static final String FULLTEXT = "full-text-search";
+ public static final String COMMENTS = "comments";
+ public static final String VRES = "vres";
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/SNLApiConfig.java b/src/main/java/org/gcube/portal/social/networking/ws/SNLApiConfig.java
new file mode 100644
index 0000000..664c935
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/SNLApiConfig.java
@@ -0,0 +1,47 @@
+package org.gcube.portal.social.networking.ws;
+
+import org.gcube.portal.social.networking.swagger.config.Bootstrap;
+
+import io.swagger.annotations.ApiKeyAuthDefinition;
+import io.swagger.annotations.ApiKeyAuthDefinition.ApiKeyLocation;
+import io.swagger.annotations.Contact;
+import io.swagger.annotations.Extension;
+import io.swagger.annotations.ExtensionProperty;
+import io.swagger.annotations.ExternalDocs;
+import io.swagger.annotations.Info;
+import io.swagger.annotations.SecurityDefinition;
+import io.swagger.annotations.SwaggerDefinition;
+
+
+@SwaggerDefinition(
+ info = @Info(
+ description = "This is the official documentation of the second version (v. 2.0) of the Social-Networking RESTful interface.",
+ version = "V 2.0",
+ title = "Social Networking RESTful Service",
+ contact = @Contact(
+ name = "Costantino Perciante",
+ email ="costantino.perciante@isti.cnr.it"
+ ),
+ extensions = {
+ @Extension(name = "extra-contact", properties = {
+ @ExtensionProperty(name = "name", value = "Massimiliano Assante"),
+ @ExtensionProperty(name = "email", value = "massimiliano.assante@isti.cnr.it")
+ }),
+ @Extension(name = "development-host", properties = {
+ @ExtensionProperty(name = "url", value = "https://socialnetworking-d-d4s.d4science.org"),
+ })
+ }
+ ),
+ externalDocs=@ExternalDocs(url="https://wiki.gcube-system.org/gcube/Social_Networking_Service", value="Wiki page at gCube"),
+ host="socialnetworking1.d4science.org",
+ basePath="social-networking-library-ws/rest",
+ securityDefinition=@SecurityDefinition(
+ apiKeyAuthDefinitions={
+ @ApiKeyAuthDefinition(key=Bootstrap.GCUBE_TOKEN_IN_QUERY_DEF, description="A token bound to a user (or app identifier) and a context", in=ApiKeyLocation.HEADER, name="gcube-token"),
+ @ApiKeyAuthDefinition(key=Bootstrap.GCUBE_TOKEN_IN_HEADER_DEF, description="A token bound to a user (or app identifier) and a context", in=ApiKeyLocation.QUERY, name="gcube-token")
+ }),
+ schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS}
+ )
+public interface SNLApiConfig {
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/WelcomeService.java b/src/main/java/org/gcube/portal/social/networking/ws/WelcomeService.java
new file mode 100644
index 0000000..48f7c50
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/WelcomeService.java
@@ -0,0 +1,19 @@
+package org.gcube.portal.social.networking.ws;
+
+import javax.inject.Singleton;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+@Path("/")
+@Singleton
+public class WelcomeService {
+
+ @GET
+ @Produces(MediaType.TEXT_HTML)
+ public Response sayHtmlHello() {
+ return Response.ok("The social networking web service is up and running!
").build();
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/ex/ApplicationException.java b/src/main/java/org/gcube/portal/social/networking/ws/ex/ApplicationException.java
new file mode 100644
index 0000000..6360754
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/ex/ApplicationException.java
@@ -0,0 +1,29 @@
+package org.gcube.portal.social.networking.ws.ex;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Exception thrown when @Valid fail
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Provider
+public class ApplicationException implements ExceptionMapper {
+
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ApplicationException.class);
+
+ public Response toResponse(Exception e) {
+ logger.warn("ApplicationException invoked for exception " + e);
+ return Response
+ .status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode())
+ .type(MediaType.APPLICATION_JSON)
+ .entity(new ResponseBean(false, e.getMessage(), null))
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/ex/ValidationException.java b/src/main/java/org/gcube/portal/social/networking/ws/ex/ValidationException.java
new file mode 100644
index 0000000..1d81a24
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/ex/ValidationException.java
@@ -0,0 +1,36 @@
+package org.gcube.portal.social.networking.ws.ex;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+
+import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Exception thrown on fail
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Provider
+public class ValidationException implements ExceptionMapper {
+
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ValidationException.class);
+ @Override
+ public Response toResponse(javax.validation.ValidationException e) {
+ final StringBuilder strBuilder = new StringBuilder();
+ for (ConstraintViolation> cv : ((ConstraintViolationException) e).getConstraintViolations()) {
+ strBuilder.append(cv.getMessage());
+ break;
+ }
+
+ logger.warn("ValidationException invoked, returning " + strBuilder.toString());
+ return Response
+ .status(Response.Status.BAD_REQUEST.getStatusCode())
+ .type(MediaType.APPLICATION_JSON)
+ .entity(new ResponseBean(false, strBuilder.toString(), null))
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/inputs/ApplicationId.java b/src/main/java/org/gcube/portal/social/networking/ws/inputs/ApplicationId.java
new file mode 100644
index 0000000..58495d9
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/inputs/ApplicationId.java
@@ -0,0 +1,51 @@
+package org.gcube.portal.social.networking.ws.inputs;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+
+/**
+ * Application id object
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@ApiModel(description="An object containing the app_id field", value="Application")
+public class ApplicationId {
+
+ @JsonProperty("app_id")
+ @NotNull(message="app_id cannot be null")
+ @Size(message="app_id cannot be empty", min=1)
+ @ApiModelProperty(
+ example="appX",
+ name="app_id",
+ required=true,
+ value="The application identifier"
+ )
+ private String appId;
+
+ public ApplicationId() {
+ super();
+ }
+
+ public ApplicationId(String appId) {
+ super();
+ this.appId = appId;
+ }
+
+ public String getAppId() {
+ return appId;
+ }
+
+ public void setAppId(String appId) {
+ this.appId = appId;
+ }
+
+ @Override
+ public String toString() {
+ return "ApplicationId [appId=" + appId + "]";
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/inputs/JobNotificationBean.java b/src/main/java/org/gcube/portal/social/networking/ws/inputs/JobNotificationBean.java
new file mode 100644
index 0000000..39a55ca
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/inputs/JobNotificationBean.java
@@ -0,0 +1,164 @@
+package org.gcube.portal.social.networking.ws.inputs;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotNull;
+
+import org.gcube.portal.databook.shared.JobStatusType;
+import org.gcube.portal.databook.shared.RunningJob;
+import org.gcube.portal.social.networking.ws.providers.JobStatusTypeDeserializer;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+
+/**
+ * The job notification bean class.
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+@JsonIgnoreProperties(ignoreUnknown = true) // ignore in serialization/deserialization
+@ApiModel(description="A job notification object", value="JobNotification")
+public class JobNotificationBean {
+
+ @JsonProperty("recipient")
+ @NotNull(message="recipient cannot be missing")
+ @ApiModelProperty(value="The recipient of the notification",
+ required=true,
+ example="andrea.rossi",
+ name="recipient")
+ private String recipient;
+
+ @JsonProperty("job_id")
+ @ApiModelProperty(value="The job's identifier, i.e. very likely a UUID",
+ required=true,
+ example="88055f18-558a-4246-942d-a43012528c92",
+ name="job_id")
+ @NotNull(message="job_id cannot be missing")
+ private String jobId;
+
+ @JsonProperty("job_name")
+ @ApiModelProperty(value="The job's name",
+ required=true,
+ example="SocialDataIndexer Plugin",
+ name="job_name")
+ @NotNull(message="job_name cannot be missing")
+ private String jobName;
+
+ @JsonProperty("service_name")
+ @ApiModelProperty(value="The name of the service running the job",
+ required=true,
+ example="SmartExecutor",
+ name="service_name")
+ @NotNull(message="service_name cannot be missing")
+ private String serviceName;
+
+ @JsonProperty("status")
+ @ApiModelProperty(value="The status of the job",
+ required=true,
+ example="FAILED",
+ name="status",
+ allowableValues = "CANCELLED,CANCELLING,DELETED,DELETING,EXECUTING,FAILED,NEW,SUBMITTED,SUCCEEDED,TIMED_OUT,WAITING"
+ )
+ @JsonDeserialize(using=JobStatusTypeDeserializer.class)
+ @NotNull(message="status cannot be missing")
+ private JobStatusType status;
+
+ @JsonProperty("status_message")
+ @ApiModelProperty(value="A message reporting the reason of the current status",
+ required=false,
+ example="An error occurred while ...",
+ name="status_message")
+ private String statusMessage;
+
+ public JobNotificationBean() {
+ super();
+ }
+
+ /**
+ * @param recipient
+ * @param jobId
+ * @param jobName
+ * @param serviceName
+ * @param status
+ * @param statusMessage
+ */
+ public JobNotificationBean(String recipient, String jobId, String jobName,
+ String serviceName, JobStatusType status, String statusMessage) {
+ super();
+ this.recipient = recipient;
+ this.jobId = jobId;
+ this.jobName = jobName;
+ this.serviceName = serviceName;
+ this.status = status;
+ this.statusMessage = statusMessage;
+ }
+
+ public String getStatusMessage() {
+ return statusMessage;
+ }
+
+ public void setStatusMessage(String statusMessage) {
+ this.statusMessage = statusMessage;
+ }
+
+ public String getRecipient() {
+ return recipient;
+ }
+
+ public void setRecipient(String recipient) {
+ this.recipient = recipient;
+ }
+
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ public void setServiceName(String serviceName) {
+ this.serviceName = serviceName;
+ }
+
+ public String getJobId() {
+ return jobId;
+ }
+
+ public void setJobId(String jobId) {
+ this.jobId = jobId;
+ }
+
+ public String getJobName() {
+ return jobName;
+ }
+
+ public void setJobName(String jobName) {
+ this.jobName = jobName;
+ }
+
+ public JobStatusType getStatus() {
+ return status;
+ }
+
+ public void setStatus(JobStatusType status) {
+ this.status = status;
+ }
+
+ public RunningJob getRunningJob(){
+
+ return new RunningJob(jobId, jobName, status, statusMessage, serviceName);
+
+ }
+
+ @Override
+ public String toString() {
+ return "JobNotificationBean ["
+ + (recipient != null ? "recipient=" + recipient + ", " : "")
+ + (jobId != null ? "jobId=" + jobId + ", " : "")
+ + (jobName != null ? "jobName=" + jobName + ", " : "")
+ + (serviceName != null ? "serviceName=" + serviceName + ", "
+ : "")
+ + (status != null ? "status=" + status + ", " : "")
+ + (statusMessage != null ? "statusMessage=" + statusMessage
+ : "") + "]";
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/inputs/MessageInputBean.java b/src/main/java/org/gcube/portal/social/networking/ws/inputs/MessageInputBean.java
new file mode 100644
index 0000000..bf2792b
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/inputs/MessageInputBean.java
@@ -0,0 +1,114 @@
+package org.gcube.portal.social.networking.ws.inputs;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Generic input bean for methods that allow to write messages
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@JsonIgnoreProperties(ignoreUnknown = true) // ignore in serialization/deserialization
+@ApiModel(description="A message object", value="Message")
+public class MessageInputBean implements Serializable {
+
+ private static final long serialVersionUID = -1317811686036127456L;
+
+ /*@JsonProperty("sender")
+ @ApiModelProperty(
+ example="andrea.rossi",
+ required=false,
+ hidden=true, // do not show this information
+ value="The sender of the message. If not specified is the gcube-token's owner")
+ private String sender;*/
+
+ @JsonProperty("body")
+ @NotNull(message="body cannot be missing")
+ @Size(min=1, message="body cannot be empty")
+ @ApiModelProperty(
+ example="This is the body of the mail ...",
+ required= true,
+ value="The body of the message")
+ private String body;
+
+ @JsonProperty("subject")
+ @NotNull(message="subject cannot be missing")
+ @Size(min=1, message="subject cannot be empty")
+ @ApiModelProperty(
+ example="This is the subject of the mail ...",
+ required= true,
+ value="The subject of the message")
+ private String subject;
+
+ @JsonProperty("recipients")
+ @NotNull(message="recipients cannot be missing")
+ @Size(min=1, message="at least a recipient is needed")
+ @Valid // validate recursively
+ @ApiModelProperty(
+ required= true,
+ value="The recipients of this message. At least one is needed")
+ private ArrayList recipients;
+
+ public MessageInputBean() {
+ super();
+ }
+
+ public MessageInputBean(String sender, String body, String subject,
+ ArrayList recipients) {
+ super();
+ //this.sender = sender;
+ this.body = body;
+ this.subject = subject;
+ this.recipients = recipients;
+ }
+
+ // public String getSender() {
+ // return sender;
+ // }
+ // public void setSender(String sender) {
+ // this.sender = sender;
+ // }
+ public String getBody() {
+ return body;
+ }
+ public void setBody(String body) {
+ this.body = body;
+ }
+ public String getSubject() {
+ return subject;
+ }
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+ public ArrayList getRecipients() {
+ return recipients;
+ }
+ public void setRecipients(ArrayList recipients) {
+ this.recipients = recipients;
+ }
+
+ @Override
+ public String toString() {
+ return "MessageInputBean ["
+ + (body != null ? "body=" + body + ", " : "")
+ + (subject != null ? "subject=" + subject + ", " : "")
+ + (recipients != null ? "recipients=" + recipients : "") + "]";
+ }
+
+ // @Override
+ // public String toString() {
+ // return "MessageInputBean [sender=" + sender + ", body=" + body
+ // + ", subject=" + subject + ", recipients=" + recipients + "]";
+ // }
+
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/inputs/PostInputBean.java b/src/main/java/org/gcube/portal/social/networking/ws/inputs/PostInputBean.java
new file mode 100644
index 0000000..4e9f76c
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/inputs/PostInputBean.java
@@ -0,0 +1,171 @@
+package org.gcube.portal.social.networking.ws.inputs;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+/**
+ * Generic input bean for methods that allow to write posts
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@JsonIgnoreProperties(ignoreUnknown = true) // ignore in serialization/deserialization
+@ApiModel(description="A post object", value="Post")
+public class PostInputBean implements Serializable{
+
+ private static final long serialVersionUID = 5274608088828232980L;
+
+ @JsonProperty("text")
+ @NotNull(message="text cannot be null")
+ @Size(min=1, message="text cannot be empty")
+ @ApiModelProperty(
+ example="Dear vre members, ...",
+ required=true,
+ value="The text of the post")
+ private String text;
+
+ @JsonProperty("preview_title")
+ @ApiModelProperty(
+ required=false,
+ value="A preview title for the preview",
+ name="preview_title")
+ private String previewtitle;
+
+ @JsonProperty("preview_description")
+ @ApiModelProperty(
+ required=false,
+ value="A preview description for the preview",
+ name="preview_description")
+ private String previewdescription;
+
+ @JsonProperty("preview_host")
+ @ApiModelProperty(
+ required=false,
+ value="A preview host for the preview",
+ name="preview_host")
+ private String previewhost;
+
+ @JsonProperty("preview_url")
+ @ApiModelProperty(
+ required=false,
+ value="A preview url for the preview",
+ name="preview_url")
+ private String previewurl;
+
+ @JsonProperty("image_url")
+ @ApiModelProperty(
+ required=false,
+ value="An image url for the preview",
+ name="image_url")
+ private String httpimageurl;
+
+ @JsonProperty("enable_notification")
+ @ApiModelProperty(
+ required=false,
+ value="If true send a notification to the other vre members about this post",
+ name="enable_notification")
+ private boolean enablenotification;
+
+ @JsonProperty("params")
+ @ApiModelProperty(
+ required=false,
+ value="Other parameters for the application's posts")
+ private String params;
+
+ public PostInputBean() {
+ super();
+ }
+
+ public PostInputBean(String text, String previewtitle,
+ String previewdescription, String previewhost, String previewurl,
+ String httpimageurl, boolean enablenotification, String params) {
+ super();
+ this.text = text;
+ this.previewtitle = previewtitle;
+ this.previewdescription = previewdescription;
+ this.previewhost = previewhost;
+ this.previewurl = previewurl;
+ this.httpimageurl = httpimageurl;
+ this.enablenotification = enablenotification;
+ this.params = params;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public String getPreviewtitle() {
+ return previewtitle;
+ }
+
+ public void setPreviewtitle(String previewtitle) {
+ this.previewtitle = previewtitle;
+ }
+
+ public String getPreviewdescription() {
+ return previewdescription;
+ }
+
+ public void setPreviewdescription(String previewdescription) {
+ this.previewdescription = previewdescription;
+ }
+
+ public String getPreviewhost() {
+ return previewhost;
+ }
+
+ public void setPreviewhost(String previewhost) {
+ this.previewhost = previewhost;
+ }
+
+ public String getPreviewurl() {
+ return previewurl;
+ }
+
+ public void setPreviewurl(String previewurl) {
+ this.previewurl = previewurl;
+ }
+
+ public String getHttpimageurl() {
+ return httpimageurl;
+ }
+
+ public void setHttpimageurl(String httpimageurl) {
+ this.httpimageurl = httpimageurl;
+ }
+
+ public boolean isEnablenotification() {
+ return enablenotification;
+ }
+
+ public void setEnablenotification(boolean enablenotification) {
+ this.enablenotification = enablenotification;
+ }
+
+ public String getParams() {
+ return params;
+ }
+
+ public void setParams(String params) {
+ this.params = params;
+ }
+
+ @Override
+ public String toString() {
+ return "PostInputBean [text=" + text + ", previewtitle=" + previewtitle
+ + ", previewdescription=" + previewdescription
+ + ", previewhost=" + previewhost + ", previewurl=" + previewurl
+ + ", httpimageurl=" + httpimageurl + ", enablenotification="
+ + enablenotification + ", params=" + params + "]";
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/inputs/Recipient.java b/src/main/java/org/gcube/portal/social/networking/ws/inputs/Recipient.java
new file mode 100644
index 0000000..e375a7b
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/inputs/Recipient.java
@@ -0,0 +1,52 @@
+package org.gcube.portal.social.networking.ws.inputs;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+/**
+ * Recipient message bean
+ * @author Costantino Perciante at ISTI-CNR
+ * (costantino.perciante@isti.cnr.it)
+ *
+ */
+@JsonIgnoreProperties(ignoreUnknown = true) // ignore in serialization/deserialization
+@ApiModel(description="A recipient object")
+public class Recipient implements Serializable{
+
+ private static final long serialVersionUID = 1071412144446514138L;
+
+ @JsonProperty("id")
+ @NotNull(message="recipient id must not be null")
+ @Size(min=1, message="recipient id must not be empty")
+ @ApiModelProperty(
+ example="andrea.rossi",
+ required=true,
+ value="The id of the recipient")
+ private String id;
+
+ public Recipient() {
+ super();
+ }
+ public Recipient(String id) {
+ super();
+ this.id = id;
+ }
+ public String getId() {
+ return id;
+ }
+ public void setId(String id) {
+ this.id = id;
+ }
+ @Override
+ public String toString() {
+ return "Recipient [id=" + id + "]";
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Messages.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Messages.java
new file mode 100644
index 0000000..c1099d1
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Messages.java
@@ -0,0 +1,146 @@
+package org.gcube.portal.social.networking.ws.methods.v1;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.applicationsupportlayer.social.ApplicationNotificationsManager;
+import org.gcube.applicationsupportlayer.social.NotificationsManager;
+import org.gcube.applicationsupportlayer.social.shared.SocialNetworkingSite;
+import org.gcube.applicationsupportlayer.social.shared.SocialNetworkingUser;
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.homelibrary.home.HomeLibrary;
+import org.gcube.common.homelibrary.home.workspace.Workspace;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.notifications.bean.GenericItemBean;
+import org.gcube.portal.notifications.thread.MessageNotificationsThread;
+import org.gcube.portal.social.networking.caches.SocialNetworkingSiteFinder;
+import org.gcube.portal.social.networking.liferay.ws.UserManagerWSBuilder;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.gcube.vomanagement.usermanagement.UserManager;
+import org.gcube.vomanagement.usermanagement.model.GCubeUser;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Messages services REST interface
+ * @author Costantino Perciante at ISTI-CNR
+ * (costantino.perciante@isti.cnr.it)
+ */
+@Path("/messages")
+@Deprecated
+public class Messages {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Messages.class);
+
+ // recipients separator
+ private static final String RECIPIENTS_ID_SEPARATOR = ",";
+
+ // user manager
+ private UserManager uManager = null;
+
+ /**
+ * Build usermanager
+ * @throws Exception
+ */
+ public Messages() throws Exception {
+ uManager = UserManagerWSBuilder.getInstance().getUserManager();
+ }
+
+ @POST
+ @Path("writeMessageToUsers/")
+ @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
+ /**
+ * Try to send a message to recipientsIds. The sender is the owner of the gcube-token if not otherwise stated.
+ * @param body
+ * @param subject
+ * @return ok on success, error otherwise
+ */
+ public Response writeMessageToUsers(
+ @FormParam("sender") String sender, // the optional sender, if missing the sender will be the token's owner.
+ @FormParam("body") String body,
+ @FormParam("subject") String subject,
+ @FormParam("recipients") String recipientsIds) {
+
+ if(body == null || body.isEmpty() || subject == null || subject.isEmpty() || recipientsIds == null || recipientsIds.isEmpty()){
+ logger.error("Missing/wrong request parameters");
+ return Response.status(Status.BAD_REQUEST).entity(ErrorMessages.MISSING_PARAMETERS).build();
+ }
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String senderId = caller.getClient().getId();
+ String scope = ScopeProvider.instance.get();
+
+ // check on sender id
+ if(sender == null || sender.isEmpty())
+ logger.info("Sender is going to be the token's owner [" + senderId + "]");
+ else{
+ logger.info("Sender is going to be " + sender);
+ senderId = sender;
+ }
+
+ // get the recipients ids (simple check, trim)
+ List recipientsListFiltered = new ArrayList();
+ String[] splittedRecipientsIds = recipientsIds.split(RECIPIENTS_ID_SEPARATOR);
+ List recipientsBeans = new ArrayList();
+ for (String recipientId : splittedRecipientsIds) {
+ try{
+ String tempId = recipientId.trim();
+ if(tempId.isEmpty())
+ continue;
+ GCubeUser userRecipient = uManager.getUserByUsername(tempId);
+ GenericItemBean beanUser = new GenericItemBean(userRecipient.getUsername(), userRecipient.getUsername(), userRecipient.getFullname(), userRecipient.getUserAvatarURL());
+ recipientsBeans.add(beanUser);
+ recipientsListFiltered.add(tempId);
+ }catch(Exception e){
+ logger.error("Unable to retrieve recipient information for recipient with id " + recipientId, e);
+ }
+ }
+
+ if(recipientsListFiltered.isEmpty()){
+ logger.error("Missing/wrong request parameters");
+ return Response.status(Status.BAD_REQUEST).entity(ErrorMessages.BAD_REQUEST).build();
+ }
+ try{
+
+ logger.info("Trying to send message with body " + body + " subject " + subject + " to user " + recipientsIds + " from " + senderId);
+
+ // sender info
+ GCubeUser senderUser = uManager.getUserByUsername(senderId);
+ Workspace workspace = HomeLibrary.getUserWorkspace(senderId);
+
+ // send message
+ logger.debug("Sending message to " + recipientsListFiltered);
+ String messageId = workspace.getWorkspaceMessageManager()
+ .sendMessageToPortalLogins(subject, body,
+ new ArrayList(), recipientsListFiltered);
+
+ // send notification
+ logger.debug("Message sent to " + recipientsIds + ". Sending message notification to: " + recipientsIds);
+ SocialNetworkingSite site = SocialNetworkingSiteFinder.getSocialNetworkingSiteFromScope(scope);
+ SocialNetworkingUser user = new SocialNetworkingUser(
+ senderUser.getUsername(), senderUser.getEmail(),
+ senderUser.getFullname(), senderUser.getUserAvatarURL());
+
+ logger.info("SocialNetworkingUser is " + user);
+
+ NotificationsManager nm = new ApplicationNotificationsManager(UserManagerWSBuilder.getInstance().getUserManager(), site, scope, user);
+ new Thread(new MessageNotificationsThread(recipientsBeans, messageId, subject, body, nm)).start();
+
+
+ }catch(Exception e){
+ logger.error("Unable to send message.", e);
+ return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ return Response.status(Status.CREATED).build();
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Notifications.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Notifications.java
new file mode 100644
index 0000000..637b206
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Notifications.java
@@ -0,0 +1,67 @@
+package org.gcube.portal.social.networking.ws.methods.v1;
+
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.portal.databook.shared.Notification;
+import org.gcube.portal.social.networking.ws.utils.CassandraConnection;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.slf4j.LoggerFactory;
+
+/**
+ * REST interface for the social networking library (notifications).
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("/notifications")
+@Deprecated
+public class Notifications {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Notifications.class);
+
+ @GET
+ @Path("getRangeNotificationsByUser/")
+ @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
+ /**
+ * Retrieves notifications from from up to a given quantity for a given user (to whom the token belongs)
+ * @param from
+ * @param quantity
+ * @param token
+ * @return
+ */
+ public Response getRangeNotificationsByUser(
+ @QueryParam("from") int from,
+ @QueryParam("quantity") int quantity) {
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+
+ logger.info("Retrieving " + quantity + " notifications of user = " + username + " from " + from);
+
+ if(from <= 0 || quantity <= 0){
+ logger.error("Missing/wrong request parameters");
+ return Response.status(Status.BAD_REQUEST).entity(ErrorMessages.BAD_REQUEST).build();
+ }
+
+ List notifications = null;
+ try{
+ notifications = CassandraConnection.getInstance().getDatabookStore().getRangeNotificationsByUser(username, from, quantity);
+ }catch(Exception e){
+ logger.error("Unable to retrieve such notifications.", e);
+ return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ logger.info("List of notifications retrieved");
+ return Response.status(Status.OK).entity(notifications).build();
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Posts.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Posts.java
new file mode 100644
index 0000000..7794376
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Posts.java
@@ -0,0 +1,436 @@
+package org.gcube.portal.social.networking.ws.methods.v1;
+
+
+import java.util.Iterator;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.FormParam;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.databook.shared.ApplicationProfile;
+import org.gcube.portal.databook.shared.Feed;
+import org.gcube.portal.databook.shared.FeedType;
+import org.gcube.portal.social.networking.liferay.ws.GroupManagerWSBuilder;
+import org.gcube.portal.social.networking.ws.utils.CassandraConnection;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.gcube.portal.social.networking.ws.utils.Filters;
+import org.gcube.portal.social.networking.ws.utils.SocialUtils;
+import org.gcube.vomanagement.usermanagement.GroupManager;
+import org.slf4j.LoggerFactory;
+
+/**
+ * REST interface for the social networking library (feeds).
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("/posts")
+@Deprecated
+public class Posts {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Posts.class);
+
+ @GET
+ @Path("getRecentPostsByUserAndDate/")
+ @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
+ /**
+ * Retrieves the most recent posts of the token's owner (starting from timeInMillis)
+ * @param timeInMillis
+ * @return Response (OK, BAD REQUEST, ...)
+ */
+ public Response getRecentPostsByUserAndDate(
+ @QueryParam("time") long timeInMillis) {
+
+ if(timeInMillis < 0){
+ logger.error("Missing/wrong request parameters");
+ return Response.status(Status.BAD_REQUEST).entity(ErrorMessages.MISSING_PARAMETERS).build();
+ }
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ List feeds = null;
+
+ try{
+ logger.info("Retrieving feeds for user id " + username + " and reference time " + timeInMillis);
+ feeds = CassandraConnection.getInstance().getDatabookStore().getRecentFeedsByUserAndDate(username, timeInMillis);
+ Filters.filterFeedsPerContext(feeds, context);
+ }catch(Exception e){
+ logger.error("Unable to retrieve such feeds.", e);
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ logger.info("List retrieved");
+ return Response.status(Status.OK).entity(feeds).build();
+ }
+
+ @GET
+ @Path("getAllPostsByUser/")
+ @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
+ /**
+ * Retrieves all posts belong to token's owner
+ * @return Response (OK, BAD REQUEST, ...)
+ */
+ public Response getAllPostsByUser() {
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+
+ List feeds = null;
+ try{
+ logger.info("Retrieving feeds for user with id " + username);
+ feeds = CassandraConnection.getInstance().getDatabookStore().getAllFeedsByUser(username);
+ Filters.filterFeedsPerContext(feeds, context);
+ }catch(Exception e){
+ logger.error("Unable to retrieve such feeds.", e);
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ logger.info("List retrieved");
+ return Response.status(Status.OK).entity(feeds).build();
+ }
+
+ @GET
+ @Path("getRecentPostsByUser/")
+ @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
+ /**
+ * Retrieves the last quantity posts belonging to token's owner
+ * @param quantity
+ * @param token
+ * @return Response (OK, BAD REQUEST, ...)
+ */
+ public Response getRecentPostsByUser(
+ @DefaultValue("10") @QueryParam("quantity") int quantity) {
+
+ if(quantity < 0){
+ logger.error("Missing/wrong request parameters");
+ return Response.status(Status.BAD_REQUEST).entity(ErrorMessages.MISSING_PARAMETERS).build();
+ }
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ List feeds = null;
+
+ // if quantity is zero, just return an empty list
+ if(quantity == 0){
+ return Response.status(Status.OK).entity(feeds).build();
+ }
+
+ try{
+ logger.info("Retrieving last " + quantity + " feeds made by user " + username);
+ feeds = CassandraConnection.getInstance().getDatabookStore().getRecentFeedsByUser(username, -1);
+ Filters.filterFeedsPerContext(feeds, context);
+ feeds = feeds.subList(0, quantity);
+ }catch(Exception e){
+ logger.error("Unable to retrieve such feeds.", e);
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ logger.info("List retrieved");
+ return Response.status(Status.OK).entity(feeds).build();
+ }
+
+ @POST
+ @Consumes("application/x-www-form-urlencoded")
+ @Path("writePostUser")
+ @Produces(MediaType.TEXT_PLAIN)
+ /**
+ * Allows a user to write post
+ * @param feedText
+ * @param previewTitle
+ * @param previewDescription
+ * @param previewHost
+ * @param previewUrl
+ * @param httpImageUrl
+ * @param enableNotification
+ * @param token
+ * @return
+ */
+ public Response writePostUser(
+ @FormParam("text") String feedText,
+ @FormParam("previewtitle")String previewTitle,
+ @FormParam("previewdescription") String previewDescription,
+ @FormParam("previewhost") String previewHost,
+ @FormParam("previewurl") String previewUrl,
+ @FormParam("httpimageurl") String httpImageUrl,
+ @DefaultValue("false") @FormParam("enablenotification") String enableNotification
+ ){
+
+ logger.info("Request of writing a feed coming from user");
+
+ // at least the feedText is necessary to write a feed (the token is needed anyway)
+ if(feedText == null || feedText.isEmpty()){
+
+ logger.error("Missing request parameters");
+ return Response.status(Status.BAD_REQUEST).entity(ErrorMessages.MISSING_PARAMETERS).build();
+
+ }
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+
+ try{
+ // check it is a VRE
+ GroupManager groupManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+ long groupId = groupManager.getGroupIdFromInfrastructureScope(context);
+ boolean isVRE = groupManager.isVRE(groupId);
+ if(!isVRE){
+ logger.error("A post cannot be written into a context that is not a VRE");
+ return Response.status(Status.BAD_REQUEST).entity(ErrorMessages.POST_OUTSIDE_VRE).build();
+ }
+
+ // convert enablenotification parameter
+ boolean notifyGroup = enableNotification.equals("true");
+ if(notifyGroup)
+ logger.info("Enable notification for this user post.");
+ else
+ logger.info("Disable notification for this user post.");
+
+
+ // try to share
+ logger.debug("Trying to share user feed...");
+ Feed res = SocialUtils.shareUserUpdate(
+ username,
+ feedText,
+ ScopeProvider.instance.get(),
+ previewTitle,
+ previewDescription,
+ previewHost,
+ previewUrl,
+ httpImageUrl,
+ notifyGroup
+ );
+
+ if(res != null){
+ logger.info("Feed correctly written by user " + username);
+ return Response.status(Status.CREATED).build();
+ }
+
+ }catch(Exception e){
+ logger.error("Feed not written by user " + username, e);
+ }
+ logger.info("Feed not written by user " + username);
+ return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+
+ }
+
+ @GET
+ @Path("getAllPostsByApp/")
+ @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
+ /**
+ * Retrieves all application's posts.
+ * @param token
+ * @return
+ */
+ public Response getAllPostsByApp() {
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String appId = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ // check if the token actually matches an application
+ ApplicationProfile appProfile = SocialUtils.getProfileFromInfrastrucure(appId, ScopeProvider.instance.get());
+ if(appProfile == null){
+ logger.error("The given token is not belonging to an application!!!");
+ return Response.status(Status.FORBIDDEN).entity(ErrorMessages.NOT_APP_TOKEN).build();
+ }
+ List feeds = null;
+ try{
+ logger.info("Retrieving feeds for app with id " + appId);
+ feeds = CassandraConnection.getInstance().getDatabookStore().getAllFeedsByApp(appId);
+ Filters.filterFeedsPerContext(feeds, context);
+ }catch(Exception e){
+ logger.error("Unable to retrieve such feeds.", e);
+ return Response.status(Status.NOT_FOUND).build();
+ }
+
+ logger.info("List retrieved");
+ return Response.status(Status.OK).entity(feeds).build();
+ }
+
+ @POST
+ @Consumes("application/x-www-form-urlencoded")
+ @Path("writePostApplication")
+ @Produces(MediaType.TEXT_PLAIN)
+ /**
+ * Allows an application to write a post.
+ * @param feedText
+ * @param uriParams
+ * @param previewTitle
+ * @param previewDescription
+ * @param httpImageUrl
+ * @param enableNotification
+ * @param token
+ * @return
+ */
+ public Response writePostApp(
+ @FormParam("text") String feedText,
+ @FormParam("params") String uriParams,
+ @FormParam("previewtitle")String previewTitle,
+ @FormParam("previewdescription") String previewDescription,
+ @FormParam("httpimageurl") String httpImageUrl,
+ @DefaultValue("false") @FormParam("enablenotification") String enableNotification
+ ){
+
+ logger.info("Request of writing a feed coming from an application.");
+
+ // at least the feedText is necessary to write a feed (the token is needed anyway)
+ if(feedText == null || feedText.isEmpty()){
+ logger.error("Missing request parameters");
+ return Response.status(Status.BAD_REQUEST).entity(ErrorMessages.MISSING_PARAMETERS).build();
+ }
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String appId = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ try{
+ // check it is a VRE
+ GroupManager groupManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+ long groupId = groupManager.getGroupIdFromInfrastructureScope(context);
+ boolean isVRE = groupManager.isVRE(groupId);
+ if(!isVRE){
+ logger.error("A post cannot be written into a context that is not a VRE");
+ return Response.status(Status.BAD_REQUEST).entity(ErrorMessages.POST_OUTSIDE_VRE).build();
+ }
+
+ // check if the token actually matches an application profile
+ ApplicationProfile appProfile = SocialUtils.getProfileFromInfrastrucure(appId, ScopeProvider.instance.get());
+ if(appProfile == null){
+ logger.error("The given token doesn't belong to an application!!!");
+ return Response.status(Status.FORBIDDEN).entity(ErrorMessages.NOT_APP_TOKEN).build();
+ }
+
+ // convert enablenotification parameter
+ boolean notifyGroup = enableNotification.equals("true");
+ if(notifyGroup)
+ logger.info("Enable notification for this application post.");
+ else
+ logger.info("Disable notification for this application post.");
+
+ // write feed + notification if it is the case
+ Feed written = SocialUtils.shareApplicationUpdate(
+ feedText,
+ uriParams,
+ previewTitle,
+ previewDescription,
+ httpImageUrl,
+ appProfile,
+ caller,
+ notifyGroup
+ );
+ if(written != null){
+ return Response.status(Status.CREATED).build();
+ }
+
+ }catch(Exception e){
+ logger.error("Error while writing a post", e);
+ }
+
+ logger.info("Feed not written by application " + appId);
+ return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ @GET
+ @Path("getAllPostsByVRE/")
+ @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
+ /**
+ * Retrieve all posts for this vre
+ * @param token a user token associated with a given vre(scope)
+ * @return
+ */
+ public Response getAllPostsByVRE() {
+
+ String scope = ScopeProvider.instance.get();
+
+ logger.info("Retrieving all posts coming from vre = " + scope);
+
+ List feeds = null;
+ try{
+ feeds = CassandraConnection.getInstance().getDatabookStore().getAllFeedsByVRE(scope);
+ Iterator it = feeds.iterator();
+
+ // remove disabled feeds
+ while (it.hasNext()) {
+ Feed f = it.next();
+
+ if(f.getType() == FeedType.DISABLED)
+ it.remove();
+
+ }
+ }catch(Exception e){
+ logger.error("Unable to retrieve feeds for vre = " + scope, e);
+ return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ logger.info("List of feeds of vre = " + scope + " retrieved");
+ return Response.status(Status.OK).entity(feeds).build();
+ }
+
+ @GET
+ @Path("getAllLikedPostIdsByUser/")
+ @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
+ /**
+ * Retrieves all liked posts ids relates to token's owner
+ * @param token
+ * @return
+ */
+ public Response getAllLikedPostIdsByUser() {
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+
+ logger.info("Retrieving all liked feeds IDS for user with id " + username);
+
+ List retrievedLikedFeeds = null;
+ try{
+ retrievedLikedFeeds = CassandraConnection.getInstance().getDatabookStore().getAllLikedFeedIdsByUser(username);
+ }catch(Exception e){
+ logger.error("Unable to read such ids of liked feeds.", e);
+ return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ logger.info("Ids of liked feeds by " + username + " retrieved");
+ return Response.status(Status.OK).entity(retrievedLikedFeeds).build();
+ }
+
+ @GET
+ @Path("getAllLikedPostsByUser/")
+ @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_PLAIN})
+ /**
+ * Returns all liked feeds of token's owner
+ * @param limit
+ * @param token
+ * @return
+ */
+ public Response getAllLikedPostsByUser(@DefaultValue("10") @QueryParam("limit") int limit) {
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+
+ logger.info("Retrieving " + limit + " liked feeds for user with id " + username);
+
+ List retrievedLikedFeeds = null;
+ try{
+ retrievedLikedFeeds = CassandraConnection.getInstance().getDatabookStore().getAllLikedFeedsByUser(username, limit);
+ }catch(Exception e){
+ logger.error("Unable to read such liked feeds.", e);
+ return Response.status(Status.INTERNAL_SERVER_ERROR).build();
+ }
+
+ logger.info("Liked feeds by " + username + " retrieved");
+ return Response.status(Status.OK).entity(retrievedLikedFeeds).build();
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Tokens.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Tokens.java
new file mode 100644
index 0000000..008a1ed
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Tokens.java
@@ -0,0 +1,78 @@
+package org.gcube.portal.social.networking.ws.methods.v1;
+
+import static org.gcube.common.authorization.client.Constants.authorizationService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ws.rs.FormParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.databook.shared.ApplicationProfile;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.gcube.portal.social.networking.ws.utils.SocialUtils;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * REST interface for the social networking library (tokens).
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("/tokens")
+@Deprecated
+public class Tokens {
+
+ private final static String DEFAULT_ROLE = "OrganizationMember";
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Tokens.class);
+
+ @POST
+ @Path("generateApplicationToken/")
+ @Produces(MediaType.TEXT_PLAIN)
+ /**
+ * Allows the owner of the token token to generate a new token for the couple (appId, vre)
+ * where vre is the same associated to the original user token.
+ * @param appId
+ * @param token
+ * @return
+ */
+ public Response generateApplicationToken(@FormParam("appid") String appId){
+
+ logger.info("Incoming request for app token generation. Appid id is " + appId);
+
+ //user scope
+ String userScope = ScopeProvider.instance.get();
+
+ // check if an application profile exists for this appId/scope (the discovery of the service will be made on the root)
+ ApplicationProfile appProfile = SocialUtils.getProfileFromInfrastrucure(appId, userScope);
+
+ if(appProfile == null){
+ logger.error("There is no application profile for this application id and scope!");
+ return Response.status(Status.FORBIDDEN).entity(ErrorMessages.NO_APP_PROFILE_FOUND).build();
+ }
+
+ logger.info("Generating token for the application with id " + appId);
+
+ List roles = new ArrayList<>();
+ roles.add(DEFAULT_ROLE);
+
+ String appToken = null;
+ try {
+ // each token is related to an identifier and the context
+ appToken = authorizationService().generateExternalServiceToken(appId);
+ } catch (Exception e) {
+ logger.error("Unable to generate token for app " + appId + " and scope " + userScope);
+ return Response.status(Status.BAD_REQUEST).entity(ErrorMessages.TOKEN_GENERATION_APP_FAILED).build();
+ }
+
+ return Response.status(Status.CREATED).entity(appToken).build();
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Users.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Users.java
new file mode 100644
index 0000000..c402760
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v1/Users.java
@@ -0,0 +1,163 @@
+package org.gcube.portal.social.networking.ws.methods.v1;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.portal.social.networking.liferay.ws.UserManagerWSBuilder;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.gcube.portal.social.networking.ws.utils.TokensUtils;
+import org.gcube.vomanagement.usermanagement.model.GCubeUser;
+import org.slf4j.LoggerFactory;
+
+/**
+ * REST interface for the social networking library (users).
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("/users")
+@Deprecated
+public class Users {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Users.class);
+
+ private static final String NOT_USER_TOKEN_CONTEXT_USED = "User's information can only be retrieved through a user token (not qualified)";
+
+ @GET
+ @Path("readCustomAttr/")
+ @Produces(MediaType.TEXT_PLAIN)
+ /**
+ * A wrapper for the user management library 's readCustomAttr method
+ * @return Response (OK, BAD REQUEST, ...)
+ */
+ public Response readCustomAttr(
+ @QueryParam("attribute") String attributeKey
+ ) {
+
+ if(attributeKey == null || attributeKey.isEmpty()){
+ logger.error("Missing/wrong request parameters");
+ return Response.status(Status.BAD_REQUEST).entity(ErrorMessages.MISSING_PARAMETERS).build();
+ }
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String toReturn;
+ if(!TokensUtils.isUserTokenDefault(caller)){
+ logger.warn(NOT_USER_TOKEN_CONTEXT_USED);
+ return Response.status(Status.FORBIDDEN).entity("User's information can only be retrieved through a user token").build();
+ }else{
+ try{
+ GCubeUser user = UserManagerWSBuilder.getInstance().getUserManager().getUserByUsername(username);
+ toReturn = (String)UserManagerWSBuilder.getInstance().getUserManager().readCustomAttr(user.getUserId(), attributeKey);
+ }catch(Exception e){
+ logger.error("Unable to retrieve attribute for user.", e);
+ return Response.status(Status.NOT_FOUND).build();
+ }
+ }
+ logger.info("Attribute " + attributeKey + " retrieved for user " + username);
+ return Response.status(Status.OK).entity(toReturn).build();
+ }
+
+ // @PUT
+ // @Path("updateCustomAttr")
+ // @Produces(MediaType.TEXT_PLAIN)
+ // /**
+ // * A wrapper for the user management library 's saveCustomAttr method
+ // * @return
+ // */
+ // public Response updateCustomAttr(
+ // @FormParam("attribute") String attributeKey,
+ // @FormParam("value") String newValue
+ // ){
+ //
+ // if(attributeKey == null || attributeKey.isEmpty() || newValue == null){
+ //
+ // logger.error("Missing/wrong request parameters");
+ // return Response.status(Status.BAD_REQUEST).entity(ErrorMessages.missingParameters).build();
+ //
+ // }
+ //
+ // Caller caller = AuthorizationProvider.instance.get();
+ // String username = caller.getClient().getId();
+ //
+ // try{
+ //
+ // GCubeUser user = userManager.getUserByUsername(username);
+ // userManager.saveCustomAttr(user.getUserId(), attributeKey, newValue);
+ //
+ // }catch(Exception e){
+ //
+ // logger.error("Unable to set attribute for user.", e);
+ // return Response.status(Status.NOT_MODIFIED).build();
+ //
+ // }
+ //
+ // return Response.status(Status.OK).build();
+ //
+ // }
+
+ @GET
+ @Path("getUserFullname")
+ @Produces(MediaType.TEXT_PLAIN)
+ /**
+ * Retrieve user's fullname
+ * @return
+ */
+ public Response getUserUsername(){
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String toReturn = null;
+ if(!TokensUtils.isUserTokenDefault(caller)){
+ logger.warn(NOT_USER_TOKEN_CONTEXT_USED);
+ return Response.status(Status.FORBIDDEN).entity("User's information can only be retrieved through a user token").build();
+ }else{
+ try{
+ GCubeUser user = UserManagerWSBuilder.getInstance().getUserManager().getUserByUsername(username);
+ toReturn = user.getFullname();
+ logger.info("Found fullname " + toReturn + " for user " + username);
+ }catch(Exception e){
+ logger.error("Unable to retrieve attribute for user.", e);
+ return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.toString()).build();
+ }
+ }
+ return Response.status(Status.OK).entity(toReturn).build();
+ }
+
+ @GET
+ @Path("getUserEmail")
+ @Produces(MediaType.TEXT_PLAIN)
+ /**
+ * Retrieve user's email
+ * @return
+ */
+ public Response getUserEmail(){
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String toReturn = null;
+ if(!TokensUtils.isUserTokenDefault(caller)){
+ logger.warn(NOT_USER_TOKEN_CONTEXT_USED);
+ return Response.status(Status.FORBIDDEN).entity("User's information can only be retrieved through a user token").build();
+ }else{
+ try{
+
+ GCubeUser user = UserManagerWSBuilder.getInstance().getUserManager().getUserByUsername(username);
+ toReturn = user.getEmail();
+ logger.info("Found email " + toReturn + " for user " + username);
+
+ }catch(Exception e){
+
+ logger.error("Unable to retrieve attribute for user.", e);
+ return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.toString()).build();
+ }
+ }
+ return Response.status(Status.OK).entity(toReturn).build();
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Comments.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Comments.java
new file mode 100644
index 0000000..b21c06b
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Comments.java
@@ -0,0 +1,125 @@
+package org.gcube.portal.social.networking.ws.methods.v2;
+
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+
+import java.util.List;
+
+import javax.validation.ValidationException;
+import javax.validation.constraints.Min;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.databook.shared.Comment;
+import org.gcube.portal.social.networking.swagger.config.Bootstrap;
+import org.gcube.portal.social.networking.swagger.config.SwaggerConstants;
+import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
+import org.gcube.portal.social.networking.ws.utils.CassandraConnection;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.gcube.portal.social.networking.ws.utils.Filters;
+import org.slf4j.LoggerFactory;
+
+/**
+ * REST interface for the social networking library (comments).
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("2/comments")
+@Api(value=SwaggerConstants.COMMENTS, authorizations={@Authorization(value = Bootstrap.GCUBE_TOKEN_IN_QUERY_DEF), @Authorization(value = Bootstrap.GCUBE_TOKEN_IN_HEADER_DEF)})
+/**
+ * Resource endpoint for Comment (version 2)
+ * @author Costantino Perciante at ISTI-CNR
+ * (costantino.perciante@isti.cnr.it)
+ */
+public class Comments {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Comments.class);
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("get-comments-user")
+ @ApiOperation(value = "Retrieve user's comments", response=ResponseBean.class, nickname="get-comments-user", notes="Retrieve the list of comments belonging to the owner of the token in the related context.")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "The list of comments is put into the 'result' field", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getCommentsUser() {
+
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+ Caller caller = AuthorizationProvider.instance.get();
+ String context = ScopeProvider.instance.get();
+ String username = caller.getClient().getId();
+ List comments = null;
+
+ try{
+ logger.info("Retrieving comments for user id " + username);
+ comments = CassandraConnection.getInstance().getDatabookStore().getRecentCommentsByUserAndDate(username, 0);
+ Filters.filterCommentsPerContext(comments, context);
+ responseBean.setResult(comments);
+ responseBean.setSuccess(true);
+ }catch(Exception e){
+ logger.error("Unable to retrieve such comments.", e);
+ responseBean.setMessage(e.getMessage());
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("get-comments-user-by-time")
+ @ApiOperation(value = "Retrieve user's comments and filter by date", notes="Retrieve comments of the gcube-token's owner in the context bound to the token itself and filter them by date",
+ response=ResponseBean.class, nickname="get-comments-user-by-time")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "The list of comments is put into the 'result' field", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getCommentsUserByTime(
+ @QueryParam("time")
+ @Min(value = 0, message="time cannot be negative")
+ @ApiParam(
+ allowableValues="range[0, infinity]",
+ required=true,
+ name="time",
+ value="the base time for filtering operation")
+ long timeInMillis
+ ) throws ValidationException{
+
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+ Caller caller = AuthorizationProvider.instance.get();
+ String context = ScopeProvider.instance.get();
+ String username = caller.getClient().getId();
+ List comments = null;
+
+ try{
+ logger.info("Retrieving comments for user id " + username);
+ comments = CassandraConnection.getInstance().getDatabookStore().getRecentCommentsByUserAndDate(username, timeInMillis);
+ Filters.filterCommentsPerContext(comments, context);
+ responseBean.setResult(comments);
+ responseBean.setMessage("");
+ responseBean.setSuccess(true);
+ }catch(Exception e){
+ logger.error("Unable to retrieve such comments.", e);
+ responseBean.setMessage(e.getMessage());
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/FullTextSearch.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/FullTextSearch.java
new file mode 100644
index 0000000..23bb40f
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/FullTextSearch.java
@@ -0,0 +1,169 @@
+package org.gcube.portal.social.networking.ws.methods.v2;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.ValidationException;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.databook.server.DatabookStore;
+import org.gcube.portal.databook.shared.EnhancedFeed;
+import org.gcube.portal.databook.shared.Feed;
+import org.gcube.portal.social.networking.liferay.ws.GroupManagerWSBuilder;
+import org.gcube.portal.social.networking.liferay.ws.UserManagerWSBuilder;
+import org.gcube.portal.social.networking.swagger.config.Bootstrap;
+import org.gcube.portal.social.networking.swagger.config.SwaggerConstants;
+import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
+import org.gcube.portal.social.networking.ws.utils.CassandraConnection;
+import org.gcube.portal.social.networking.ws.utils.ElasticSearchConnection;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.gcube.portal.social.networking.ws.utils.Filters;
+import org.gcube.portal.social.networking.ws.utils.TokensUtils;
+import org.gcube.vomanagement.usermanagement.GroupManager;
+import org.gcube.vomanagement.usermanagement.UserManager;
+import org.gcube.vomanagement.usermanagement.model.GCubeGroup;
+import org.gcube.vomanagement.usermanagement.model.GCubeUser;
+import org.slf4j.LoggerFactory;
+
+/**
+ * REST interface for the social networking library (post and its comments).
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("2/full-text-search")
+@Api(value=SwaggerConstants.FULLTEXT, authorizations={@Authorization(value = Bootstrap.GCUBE_TOKEN_IN_QUERY_DEF), @Authorization(value = Bootstrap.GCUBE_TOKEN_IN_HEADER_DEF)})
+public class FullTextSearch {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(FullTextSearch.class);
+
+ @GET
+ @Path("search-by-query")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve posts/comments that match the given query",
+ notes="The posts/comments returned belong to the context bound to the gcube-token",
+ response=ResponseBean.class, nickname="search-by-query")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successful retrieval of posts/comments that match the query, reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response searchByQuery(
+ @Context HttpServletRequest httpServletRequest,
+ @QueryParam("query") @NotNull(message="query cannot be null") @Size(min=1, message="query cannot be empty")
+ @ApiParam(required=true, defaultValue="none", name="query", value="A string to search for")
+ String query,
+
+ @DefaultValue("0") @QueryParam("from") @Min(value=0, message="from cannot be negative")
+ @ApiParam(allowableValues="range[0, infinity]",
+ required=false, name="from", value="the index of the base result to be returned")
+ int from,
+
+ @DefaultValue("10") @QueryParam("quantity") @Min(value=0, message="quantity cannot be negative")
+ @ApiParam(allowableValues="range[1, infinity]",
+ required=false, name="quantity", value="defines how many results are most are to be returned")
+ int quantity
+ ) throws ValidationException{
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.BAD_REQUEST;
+ responseBean.setMessage("This method can be only invoked by using a user token.");
+
+ if(!TokensUtils.isUserToken(caller))
+ return Response.status(status).entity(responseBean).build();
+
+ status = Status.OK;
+ responseBean.setMessage(null);
+
+ GroupManager groupManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+
+ try{
+ // Retrieve user's vres in which we must search
+ Set vres = new HashSet();
+
+ // get the group id from the current context
+ long currentGroupId = groupManager.getGroupIdFromInfrastructureScope(context);
+ GCubeUser currUser = userManager.getUserByUsername(username);
+ List userContexts = groupManager.listGroupsByUser(currUser.getUserId());
+
+ if (groupManager.isRootVO(currentGroupId)) {
+ for (GCubeGroup group : groupManager.listGroupsByUser(currUser.getUserId())) {
+ if (groupManager.isVRE(group.getGroupId()) && userContexts.contains(group)) {
+ vres.add(groupManager.getInfrastructureScope(group.getGroupId()));
+ }
+ }
+ }
+ else if(groupManager.isVO(currentGroupId)){
+ for (GCubeGroup group : groupManager.listGroupsByUser(currUser.getUserId())) {
+ if (groupManager.isVRE(group.getGroupId()) && group.getParentGroupId() == currentGroupId && userContexts.contains(group)) {
+ vres.add(groupManager.getInfrastructureScope(group.getGroupId()));
+ }
+ }
+ }
+ else {
+ vres.add(context);
+ }
+
+ // query elastic search
+ List enhancedFeeds = ElasticSearchConnection.getSingleton().getElasticSearchClient().search(query, vres, from, quantity);
+ Filters.hideSensitiveInformation(enhancedFeeds, caller.getClient().getId());
+ DatabookStore store = CassandraConnection.getInstance().getDatabookStore();
+
+ // retrieve the ids of liked feeds by the user
+ List likedFeeds = store.getAllLikedFeedIdsByUser(username);
+
+ // update fields "liked" and "isuser"
+ for (EnhancedFeed enhancedFeed : enhancedFeeds) {
+ if(isUsers(enhancedFeed.getFeed(), username))
+ enhancedFeed.setUsers(true);
+ if(likedFeeds.contains(enhancedFeed.getFeed().getKey()))
+ enhancedFeed.setLiked(true);
+ }
+ responseBean.setResult((ArrayList) enhancedFeeds);
+ responseBean.setSuccess(true);
+ }catch(Exception e){
+ logger.error("Something went wrong while searching", e);
+ responseBean.setMessage(e.getMessage());
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+
+ }
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ /**
+ * tell if a feed belongs to the current user or not
+ * @param tocheck
+ * @param username
+ * @return true if this feed is of the current user
+ */
+ private static final boolean isUsers(Feed tocheck, String username) {
+ return (tocheck.getEntityId().equals(username));
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/HashTags.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/HashTags.java
new file mode 100644
index 0000000..d8e21d4
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/HashTags.java
@@ -0,0 +1,79 @@
+package org.gcube.portal.social.networking.ws.methods.v2;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.databook.server.DatabookStore;
+import org.gcube.portal.social.networking.liferay.ws.GroupManagerWSBuilder;
+import org.gcube.portal.social.networking.swagger.config.Bootstrap;
+import org.gcube.portal.social.networking.swagger.config.SwaggerConstants;
+import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
+import org.gcube.portal.social.networking.ws.utils.CassandraConnection;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.slf4j.LoggerFactory;
+
+/**
+ * REST interface for the social networking library (hash tags).
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("2/hashtags")
+@Api(value=SwaggerConstants.HASHTAGS, authorizations={@Authorization(value = Bootstrap.GCUBE_TOKEN_IN_QUERY_DEF), @Authorization(value = Bootstrap.GCUBE_TOKEN_IN_HEADER_DEF)})
+public class HashTags {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(HashTags.class);
+
+ @GET
+ @Path("get-hashtags-and-occurrences/")
+ @Produces({MediaType.APPLICATION_JSON})
+ @ApiOperation(value = "Retrieve hashtags", nickname="get-hashtags-and-occurrences",
+ notes="Retrieve hashtags in the context bound to the gcube-token", response=ResponseBean.class)
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Hashtags and occurrences retrieved, reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getHashTagsAndOccurrences(){
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ logger.info("User " + username + " has requested hashtags of context " + context);
+
+ try{
+ DatabookStore datastore = CassandraConnection.getInstance().getDatabookStore();
+ // TODO handle the case of VO and ROOT
+ boolean isVRE = GroupManagerWSBuilder.getInstance().getGroupManager().isVRE(GroupManagerWSBuilder.getInstance().getGroupManager().getGroupIdFromInfrastructureScope(context));
+ if(isVRE){
+ Map map = datastore.getVREHashtagsWithOccurrence(context);
+ responseBean.setResult(map);
+ responseBean.setSuccess(true);
+ }else{
+ responseBean.setMessage("Please provide a VRE token. VO and ROOT VO cases are not yet managed.");
+ responseBean.setResult(false);
+ }
+ }catch(Exception e){
+ logger.error("Failed to retrieve hashtags", e);
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Messages.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Messages.java
new file mode 100644
index 0000000..6883699
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Messages.java
@@ -0,0 +1,233 @@
+package org.gcube.portal.social.networking.ws.methods.v2;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.ValidationException;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.applicationsupportlayer.social.ApplicationNotificationsManager;
+import org.gcube.applicationsupportlayer.social.NotificationsManager;
+import org.gcube.applicationsupportlayer.social.shared.SocialNetworkingSite;
+import org.gcube.applicationsupportlayer.social.shared.SocialNetworkingUser;
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.provider.SecurityTokenProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.homelibrary.home.HomeLibrary;
+import org.gcube.common.homelibrary.home.workspace.Workspace;
+import org.gcube.common.homelibrary.home.workspace.sharing.WorkspaceMessage;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.notifications.bean.GenericItemBean;
+import org.gcube.portal.notifications.thread.MessageNotificationsThread;
+import org.gcube.portal.social.networking.caches.SocialNetworkingSiteFinder;
+import org.gcube.portal.social.networking.liferay.ws.LiferayJSONWsCredentials;
+import org.gcube.portal.social.networking.liferay.ws.UserManagerWSBuilder;
+import org.gcube.portal.social.networking.swagger.config.Bootstrap;
+import org.gcube.portal.social.networking.swagger.config.SwaggerConstants;
+import org.gcube.portal.social.networking.ws.inputs.MessageInputBean;
+import org.gcube.portal.social.networking.ws.inputs.Recipient;
+import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.gcube.portal.social.networking.ws.utils.TokensUtils;
+import org.gcube.vomanagement.usermanagement.exception.UserManagementSystemException;
+import org.gcube.vomanagement.usermanagement.exception.UserRetrievalFault;
+import org.gcube.vomanagement.usermanagement.model.GCubeUser;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Messages services REST interface
+ * @author Costantino Perciante at ISTI-CNR
+ * (costantino.perciante@isti.cnr.it)
+ */
+@Path("2/messages")
+@Api(value=SwaggerConstants.MESSAGES, authorizations={@Authorization(value = Bootstrap.GCUBE_TOKEN_IN_QUERY_DEF), @Authorization(value = Bootstrap.GCUBE_TOKEN_IN_HEADER_DEF)})
+public class Messages {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Messages.class);
+
+ @POST
+ @Path("write-message/")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Write a message to another user", notes="Write a message to another user. The sender is the token's owner by default",
+ response=ResponseBean.class, nickname="write-message")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successful write a message. Its id is reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response writeMessage(
+ @NotNull(message="Message to send is missing")
+ @Valid
+ @ApiParam(name="input", required=true, allowMultiple=false, value="The message to write")
+ MessageInputBean input,
+ @Context HttpServletRequest httpServletRequest) throws ValidationException, UserManagementSystemException, UserRetrievalFault{
+
+ logger.debug("Incoming message bean is " + input);
+
+ Caller caller = AuthorizationProvider.instance.get();
+
+ // check if the token belongs to an application token. In this case use J.A.R.V.I.S (the username used to communicate with Liferay)
+ String senderId = null;
+ if(!TokensUtils.isUserToken(caller)){
+ GCubeUser jarvis = UserManagerWSBuilder.getInstance().getUserManager().getUserByEmail(LiferayJSONWsCredentials.getSingleton().getUser());
+ SecurityTokenProvider.instance.set(LiferayJSONWsCredentials.getSingleton().getNotifierUserToken());
+ senderId = jarvis.getUsername();
+ }else{
+ senderId = caller.getClient().getId();
+ }
+ String scope = ScopeProvider.instance.get();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.CREATED;
+ String body = input.getBody();
+ String subject = input.getSubject();
+ List recipientsIds = input.getRecipients(); // "recipients":[{"recipient":"id recipient"}, ......]
+ logger.info("Sender is going to be the token's owner [" + senderId + "]");
+
+ // get the recipients ids (simple check, trim)
+ List recipientsListFiltered = new ArrayList();
+ List recipientsBeans = new ArrayList();
+ for (Recipient recipientId : recipientsIds) {
+ try{
+ String tempId = recipientId.getId().trim();
+ if(tempId.isEmpty())
+ continue;
+ GCubeUser userRecipient = UserManagerWSBuilder.getInstance().getUserManager().getUserByUsername(tempId);
+ if(userRecipient == null)
+ userRecipient = UserManagerWSBuilder.getInstance().getUserManager().getUserByEmail(tempId);
+ if(userRecipient != null){
+ GenericItemBean beanUser = new GenericItemBean(userRecipient.getUsername(), userRecipient.getUsername(), userRecipient.getFullname(), userRecipient.getUserAvatarURL());
+ recipientsBeans.add(beanUser);
+ recipientsListFiltered.add(userRecipient.getUsername());
+ }
+ }catch(Exception e){
+ logger.error("Unable to retrieve recipient information for recipient with id " + recipientId, e);
+ }
+ }
+
+ if(recipientsListFiltered.isEmpty()){
+ logger.error("Missing/wrong request parameters");
+ status = Status.BAD_REQUEST;
+ responseBean.setMessage(ErrorMessages.MISSING_PARAMETERS);
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ try{
+
+ logger.debug("Trying to send message with body " + body + " subject " + subject + " to users " + recipientsIds + " from " + senderId);
+
+ // sender info
+ GCubeUser senderUser = UserManagerWSBuilder.getInstance().getUserManager().getUserByUsername(senderId);
+ Workspace workspace = HomeLibrary.getUserWorkspace(senderId);
+
+ logger.debug("Workspace is " + workspace.getRoot());
+
+ // send message
+ logger.debug("Sending message to " + recipientsListFiltered);
+ String messageId = workspace.getWorkspaceMessageManager()
+ .sendMessageToPortalLogins(subject, body,
+ new ArrayList(), recipientsListFiltered);
+
+ // send notification
+ logger.debug("Message sent to " + recipientsIds + ". Sending message notification to: " + recipientsIds);
+ SocialNetworkingSite site = SocialNetworkingSiteFinder.getSocialNetworkingSiteFromScope(scope);
+ SocialNetworkingUser user = new SocialNetworkingUser(
+ senderUser.getUsername(), senderUser.getEmail(),
+ senderUser.getFullname(), senderUser.getUserAvatarURL());
+
+ NotificationsManager nm = new ApplicationNotificationsManager(UserManagerWSBuilder.getInstance().getUserManager(), site, ScopeProvider.instance.get(), user);
+ new Thread(new MessageNotificationsThread(recipientsBeans, messageId, subject, body, nm)).start();
+ responseBean.setSuccess(true);
+ responseBean.setResult(messageId);
+
+ }catch(Exception e){
+ logger.error("Unable to send message.", e);
+ status = Status.INTERNAL_SERVER_ERROR;
+ responseBean.setMessage(e.toString());
+ }
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-sent-messages")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve the list of sent messages", notes="Retrieve the list of sent messages. The user is the token's owner by default",
+ response=ResponseBean.class, nickname="get-sent-messages")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successful read of the sent messages, reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getSentMessages(){
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ logger.info("Request for retrieving sent messages by " + username);
+
+ try{
+ Workspace workspace = HomeLibrary.getUserWorkspace(username);
+ List sentMessages = workspace.getWorkspaceMessageManager().getSentMessages();
+ Collections.reverse(sentMessages);
+ responseBean.setSuccess(true);
+ logger.debug("Result is " + sentMessages);
+ responseBean.setResult(sentMessages);
+ }catch(Exception e){
+ logger.error("Unable to retrieve sent messages", e);
+ responseBean.setMessage(e.getMessage());
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-received-messages")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve the list of received messages", notes="Retrieve the list of received messages. The user is the token's owner by default",
+ response=ResponseBean.class, nickname="get-received-messages")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successful read of the received messages, reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getReceivedMessages(){
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ logger.info("Request for retrieving received messages by " + username);
+ try{
+ Workspace workspace = HomeLibrary.getUserWorkspace(username);
+ List getMessages = workspace.getWorkspaceMessageManager().getReceivedMessages();
+ Collections.reverse(getMessages);
+ responseBean.setSuccess(true);
+ responseBean.setResult(getMessages);
+
+ }catch(Exception e){
+ logger.error("Unable to retrieve sent messages", e);
+ responseBean.setMessage(e.getMessage());
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Notifications.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Notifications.java
new file mode 100644
index 0000000..4881c12
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Notifications.java
@@ -0,0 +1,154 @@
+package org.gcube.portal.social.networking.ws.methods.v2;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.validation.Valid;
+import javax.validation.ValidationException;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.applicationsupportlayer.social.ApplicationNotificationsManager;
+import org.gcube.applicationsupportlayer.social.NotificationsManager;
+import org.gcube.applicationsupportlayer.social.shared.SocialNetworkingSite;
+import org.gcube.applicationsupportlayer.social.shared.SocialNetworkingUser;
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.databook.shared.Notification;
+import org.gcube.portal.notifications.bean.GenericItemBean;
+import org.gcube.portal.notifications.thread.JobStatusNotificationThread;
+import org.gcube.portal.social.networking.caches.SocialNetworkingSiteFinder;
+import org.gcube.portal.social.networking.liferay.ws.LiferayJSONWsCredentials;
+import org.gcube.portal.social.networking.liferay.ws.UserManagerWSBuilder;
+import org.gcube.portal.social.networking.swagger.config.Bootstrap;
+import org.gcube.portal.social.networking.swagger.config.SwaggerConstants;
+import org.gcube.portal.social.networking.ws.inputs.JobNotificationBean;
+import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
+import org.gcube.portal.social.networking.ws.utils.CassandraConnection;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.gcube.vomanagement.usermanagement.model.GCubeUser;
+import org.slf4j.LoggerFactory;
+
+/**
+ * REST interface for the social networking library (notifications).
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("2/notifications")
+@Api(value=SwaggerConstants.NOTIFICATIONS, authorizations={@Authorization(value = Bootstrap.GCUBE_TOKEN_IN_QUERY_DEF), @Authorization(value = Bootstrap.GCUBE_TOKEN_IN_HEADER_DEF)})
+public class Notifications {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Notifications.class);
+
+ @GET
+ @Path("get-range-notifications/")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve user's notifications", notes="Retrieve notifications of the gcube-token's owner",
+ response=ResponseBean.class, nickname="get-range-notifications")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Notifications retrieved and reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getRangeNotifications(
+ @DefaultValue("1") @QueryParam("from") @Min(value=1, message="from must be greater or equal to 1")
+ @ApiParam(allowableValues="range[0, infinity]",
+ required=false, allowMultiple=false, value="The base index notification")
+ int from,
+
+ @DefaultValue("10") @QueryParam("quantity") @Min(value=0, message="quantity must be greater or equal to 0")
+ @ApiParam(allowableValues="range[1, infinity]",
+ required=false, allowMultiple=false, value="Retrieve notifications up to this quantity")
+ int quantity
+ ) throws ValidationException{
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+
+ logger.debug("Retrieving " + quantity + " notifications of user = " + username + " from " + from);
+
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ List notifications = null;
+ try{
+ notifications = CassandraConnection.getInstance().getDatabookStore().getRangeNotificationsByUser(username, from, quantity);
+ responseBean.setResult(notifications);
+ responseBean.setSuccess(true);
+ logger.debug("List of notifications retrieved");
+ }catch(Exception e){
+ logger.error("Unable to retrieve such notifications.", e);
+ responseBean.setMessage(e.getMessage());
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @POST
+ @Path("notify-job-status/")
+ @ApiOperation(value = "Send a JOB Notification", notes="Send a JOB notification to a given recipient",
+ response=ResponseBean.class, nickname="notify-job-status")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Notification is sent correctly", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response notifyJobStatus(
+ @NotNull(message="input is missing")
+ @ApiParam(name="job", required=true, allowMultiple=false, value="The job bean")
+ @Valid JobNotificationBean job) throws ValidationException{
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String context = ScopeProvider.instance.get();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ String appQualifier = caller.getClient().getId();
+ logger.info("Received request from app " + appQualifier + " to notify job status described by bean " + job);
+
+ try{
+
+ String recipient = job.getRecipient();
+ GCubeUser userRecipient = UserManagerWSBuilder.getInstance().getUserManager().getUserByUsername(recipient);
+ GenericItemBean recipientBean = new GenericItemBean(userRecipient.getUsername(), userRecipient.getUsername(), userRecipient.getFullname(), userRecipient.getUserAvatarURL());
+
+ // notifications are sent by using the user allowed to use liferay's json apis
+ SocialNetworkingSite site = SocialNetworkingSiteFinder.getSocialNetworkingSiteFromScope(context);
+ GCubeUser senderUser = UserManagerWSBuilder.getInstance().getUserManager().getUserByEmail(LiferayJSONWsCredentials.getSingleton().getUser());
+ SocialNetworkingUser user = new SocialNetworkingUser(senderUser.getUsername(), senderUser.getEmail(), senderUser.getFullname(), senderUser.getUserAvatarURL());
+ NotificationsManager nm = new ApplicationNotificationsManager(UserManagerWSBuilder.getInstance().getUserManager(), site, context, user);
+
+ new Thread(new JobStatusNotificationThread(job.getRunningJob(), Arrays.asList(recipientBean), nm)).start();
+ responseBean.setSuccess(true);
+ responseBean.setResult("Notification thread started");
+
+ }catch(Exception e){
+ logger.error("Unable to send job notification", e);
+ responseBean.setSuccess(false);
+ responseBean.setMessage(e.getMessage());
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/People.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/People.java
new file mode 100644
index 0000000..77ab0b8
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/People.java
@@ -0,0 +1,102 @@
+package org.gcube.portal.social.networking.ws.methods.v2;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.social.networking.liferay.ws.GroupManagerWSBuilder;
+import org.gcube.portal.social.networking.liferay.ws.RoleManagerWSBuilder;
+import org.gcube.portal.social.networking.liferay.ws.UserManagerWSBuilder;
+import org.gcube.portal.social.networking.swagger.config.Bootstrap;
+import org.gcube.portal.social.networking.swagger.config.SwaggerConstants;
+import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.gcube.portal.social.networking.ws.utils.TokensUtils;
+import org.gcube.vomanagement.usermanagement.GroupManager;
+import org.gcube.vomanagement.usermanagement.RoleManager;
+import org.gcube.vomanagement.usermanagement.UserManager;
+import org.gcube.vomanagement.usermanagement.model.GCubeRole;
+import org.gcube.vomanagement.usermanagement.model.GCubeUser;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * REST interface for the social networking library (people). Used by OAUTH 2.0 apps/users.
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("2/people")
+@Api(value=SwaggerConstants.PEOPLE, authorizations={@Authorization(value = Bootstrap.GCUBE_TOKEN_IN_QUERY_DEF), @Authorization(value = Bootstrap.GCUBE_TOKEN_IN_HEADER_DEF)})
+public class People {
+
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(People.class);
+
+ @GET
+ @Path("profile")
+ @ApiOperation(value = "Retrieve user's profile", notes="Retrieve the user's profile. The user in this case is the one bound to the token which can be of any kind (qualified, default)",
+ response=ResponseBean.class, nickname="profile")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successful retrieval of user's profile, reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getProfile(){
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ GCubeUser user = null;
+ String scope = ScopeProvider.instance.get();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ if(!TokensUtils.isUserToken(caller)){
+ status = Status.FORBIDDEN;
+ responseBean.setMessage("User's information can only be retrieved through a user token");
+ logger.warn("Trying to access users method via a token different than 'user-token' is not allowed");
+ }else{
+
+ try{
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ RoleManager roleManager = RoleManagerWSBuilder.getInstance().getRoleManager();
+ GroupManager groupManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+ user = userManager.getUserByUsername(username);
+
+ Map toReturn = new HashMap();
+ toReturn.put("username", user.getUsername());
+ toReturn.put("avatar", user.getUserAvatarURL());
+ toReturn.put("fullname", user.getFullname());
+ List roles = roleManager.listRolesByUserAndGroup(user.getUserId(), groupManager.getGroupIdFromInfrastructureScope(scope));
+ List rolesNames = new ArrayList();
+ for (GCubeRole gCubeRole : roles) {
+ rolesNames.add(gCubeRole.getRoleName());
+ }
+ toReturn.put("roles", rolesNames);
+
+ responseBean.setResult(toReturn);
+ responseBean.setSuccess(true);
+ }catch(Exception e){
+ logger.error("Unable to retrieve user's profile", e);
+ responseBean.setMessage(e.getMessage());
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Posts.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Posts.java
new file mode 100644
index 0000000..8cccf06
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Posts.java
@@ -0,0 +1,509 @@
+package org.gcube.portal.social.networking.ws.methods.v2;
+
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.validation.Valid;
+import javax.validation.ValidationException;
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.databook.server.DatabookStore;
+import org.gcube.portal.databook.shared.ApplicationProfile;
+import org.gcube.portal.databook.shared.Feed;
+import org.gcube.portal.social.networking.swagger.config.Bootstrap;
+import org.gcube.portal.social.networking.swagger.config.SwaggerConstants;
+import org.gcube.portal.social.networking.ws.inputs.PostInputBean;
+import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
+import org.gcube.portal.social.networking.ws.utils.CassandraConnection;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.gcube.portal.social.networking.ws.utils.Filters;
+import org.gcube.portal.social.networking.ws.utils.SocialUtils;
+import org.slf4j.LoggerFactory;
+
+/**
+ * REST interface for the social networking library (feeds).
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("2/posts")
+@Api(value=SwaggerConstants.POSTS, authorizations={@Authorization(value = Bootstrap.GCUBE_TOKEN_IN_QUERY_DEF), @Authorization(value = Bootstrap.GCUBE_TOKEN_IN_HEADER_DEF)})
+public class Posts {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Posts.class);
+
+ @GET
+ @Path("get-posts-user-since/")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve user's posts", notes="Retrieve posts of the gcube-token's owner, and allow to filter them by time",
+ response=ResponseBean.class, nickname="get-posts-user-since")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successful retrieval of posts, reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getRecentPostsByUserAndDate(
+ @QueryParam("time") @Min(value = 0, message="time cannot be negative")
+ @ApiParam(allowableValues="range[0, infinity]", name="time",
+ required=true, allowMultiple=false, value="The reference time since when retrieving posts")
+ long timeInMillis
+ ) throws ValidationException{
+
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+ Caller caller = AuthorizationProvider.instance.get();
+ String context = ScopeProvider.instance.get();
+ String username = caller.getClient().getId();
+ List feeds = null;
+
+ try{
+ logger.info("Retrieving feeds for user id " + username + " and reference time " + timeInMillis);
+ feeds = CassandraConnection.getInstance().getDatabookStore().getRecentFeedsByUserAndDate(username, timeInMillis);
+ Filters.filterFeedsPerContext(feeds, context);
+ Filters.hideSensitiveInformation(feeds, caller.getClient().getId());
+ responseBean.setResult(feeds);
+ responseBean.setMessage("");
+ responseBean.setSuccess(true);
+
+ }catch(Exception e){
+ logger.error("Unable to retrieve such feeds.", e);
+ responseBean.setMessage(e.getMessage());
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-posts-user/")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve all user's posts", notes="Retrieve all posts of the gcube-token's owner",
+ response=ResponseBean.class, nickname="get-posts-user")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successful retrieval of posts, reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getAllPostsByUser() {
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+ List feeds = null;
+ try{
+ logger.debug("Retrieving feeds for user with id " + username);
+ feeds = CassandraConnection.getInstance().getDatabookStore().getAllFeedsByUser(username);
+ Filters.filterFeedsPerContext(feeds, context);
+ Filters.hideSensitiveInformation(feeds, caller.getClient().getId());
+ responseBean.setResult(feeds);
+ responseBean.setMessage("");
+ responseBean.setSuccess(true);
+ }catch(Exception e){
+ logger.error("Unable to retrieve such feeds.", e);
+ responseBean.setMessage(e.getMessage());
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-posts-user-quantity/")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve a given quantity of latest user's posts", notes="Retrieve a given quantity of latest posts of the gcube-token's owner",
+ response=ResponseBean.class, nickname="get-posts-user-quantity")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successful retrieval of posts, reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getQuantityPostsByUser(
+ @DefaultValue("10")
+ @QueryParam("quantity")
+ @Min(value=0, message="quantity cannot be negative")
+ @ApiParam(allowableValues="range[0, infinity]", name="quantity",
+ required=false, allowMultiple=false, value="How many posts needs to be retrieved at most")
+ int quantity) throws ValidationException{
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ List feeds = new ArrayList();
+
+ // if quantity is zero, just return an empty list
+ if(quantity == 0){
+ responseBean.setSuccess(true);
+ return Response.status(status).entity(responseBean).build();
+ }
+ try{
+ logger.debug("Retrieving last " + quantity + " feeds made by user " + username);
+ feeds = CassandraConnection.getInstance().getDatabookStore().getRecentFeedsByUser(username, -1);
+ Filters.filterFeedsPerContext(feeds, context);
+ feeds = feeds.subList(0, quantity);
+ Filters.hideSensitiveInformation(feeds, caller.getClient().getId());
+ responseBean.setResult(feeds);
+ responseBean.setSuccess(true);
+ }catch(Exception e){
+ logger.error("Unable to retrieve such feeds.", e);
+ responseBean.setMessage(e.getMessage());
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @POST
+ @Path("write-post-user")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create a new user post", notes="Create a new user post having as owner the gcube-token's owner",
+ response=ResponseBean.class, nickname="write-post-user")
+ @ApiResponses(value = {
+ @ApiResponse(code = 201, message = "Successfull created, the new post is reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response writePostUser(
+ @NotNull(message="Post to write is missing")
+ @Valid
+ @ApiParam(name="post",
+ required=true, allowMultiple=false, value="The post to be written")
+ PostInputBean post) throws ValidationException{
+
+ logger.debug("Request of writing a feed coming from user " + post);
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.CREATED;
+
+ // parse
+ String feedText = post.getText();
+ String previewTitle = post.getPreviewtitle();
+ String previewDescription = post.getPreviewdescription();
+ String previewHost = post.getPreviewhost();
+ String previewUrl = post.getPreviewurl();
+ String httpImageUrl = post.getHttpimageurl();
+ boolean enableNotification = post.isEnablenotification();
+
+ // convert enablenotification parameter
+ if(enableNotification)
+ logger.info("Enable notification for this user post.");
+ else
+ logger.info("Disable notification for this user post.");
+
+
+ // try to share
+ logger.debug("Trying to share user feed...");
+ Feed res = SocialUtils.shareUserUpdate(
+ username,
+ feedText,
+ context,
+ previewTitle,
+ previewDescription,
+ previewHost,
+ previewUrl,
+ httpImageUrl,
+ enableNotification
+ );
+
+ if(res != null){
+ logger.debug("Feed correctly written by user " + username);
+ responseBean.setResult(res);
+ responseBean.setSuccess(true);
+ return Response.status(status).entity(responseBean).build();
+
+ }
+
+ logger.error("Unable to write post.");
+ responseBean.setMessage("Unable to write post");
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+ return Response.status(status).entity(responseBean).build();
+
+ }
+
+ @GET
+ @Path("get-posts-app/")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve the application's posts", notes="Retrieve the application's posts belonging to the gcube-token's owner (i.e., an application)",
+ response=ResponseBean.class, nickname="get-posts-app")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successfull retrieved posts, they are reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 403, message = "There is no application profile with such token", response=ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getAllPostsByApp() {
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String appId = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ // check if the token actually matches an application
+ ApplicationProfile appProfile = SocialUtils.getProfileFromInfrastrucure(appId, context);
+ if(appProfile == null){
+
+ logger.error("The given token is not belonging to an application!!!");
+ status = Status.FORBIDDEN;
+ responseBean.setSuccess(false);
+ responseBean.setMessage(ErrorMessages.NOT_APP_TOKEN);
+ return Response.status(status).entity(responseBean).build();
+
+ }
+ try{
+
+ logger.debug("Retrieving feeds for app with id " + appId);
+ List feeds = CassandraConnection.getInstance().getDatabookStore().getAllFeedsByApp(appId);
+ Filters.filterFeedsPerContext(feeds, context);
+ responseBean.setSuccess(true);
+ responseBean.setResult(feeds);
+
+ }catch(Exception e){
+
+ logger.error("Unable to retrieve such feeds.", e);
+ responseBean.setMessage(e.getMessage());
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @POST
+ @Path("write-post-app")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Create a new application post", notes="Create a new application post having as owner-application the gcube-token's owner",
+ response=ResponseBean.class, nickname="write-post-app")
+ @ApiResponses(value = {
+ @ApiResponse(code = 201, message = "Successfull created, the new post is reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 403, message = "There is no application profile with such token", response=ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response writePostApp(
+ @NotNull(message="Post to write is null")
+ @Valid
+ @ApiParam(name="post",
+ required=true, allowMultiple=false, value="The post to be written")
+ PostInputBean post){
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String appId = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.CREATED;
+
+ logger.debug("Request of writing a feed coming from an application.");
+
+ // check if the token actually matches an application profile
+ ApplicationProfile appProfile = SocialUtils.getProfileFromInfrastrucure(appId, context);
+
+ if(appProfile == null){
+ logger.error("The given token doesn't belong to an application!!!");
+ responseBean.setSuccess(false);
+ responseBean.setMessage(ErrorMessages.NOT_APP_TOKEN);
+ status = Status.FORBIDDEN;
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ // parse
+ String feedText = post.getText();
+ String previewTitle = post.getPreviewtitle();
+ String previewDescription = post.getPreviewdescription();
+ String httpImageUrl = post.getHttpimageurl();
+ boolean enableNotification = post.isEnablenotification();
+ String params = post.getParams();
+
+ // convert enablenotification parameter
+ if(enableNotification)
+ logger.debug("Enable notification for this application post.");
+ else
+ logger.debug("Disable notification for this application post.");
+
+ // write feed + notification if it is the case
+ Feed written = SocialUtils.shareApplicationUpdate(
+ feedText,
+ params,
+ previewTitle,
+ previewDescription,
+ httpImageUrl,
+ appProfile,
+ caller,
+ enableNotification
+ );
+
+ if(written != null){
+ responseBean.setResult(written);
+ responseBean.setSuccess(true);
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ logger.error("Unable to write post.");
+ responseBean.setMessage("Unable to write post");
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-posts-vre/")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve vre's posts", notes="Retrieve all the posts in the context bound to the gcube-token",
+ response=ResponseBean.class, nickname="get-posts-vre")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successfull retrieved posts, they are reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getAllPostsByVRE() {
+
+ String scope = ScopeProvider.instance.get();
+ Caller caller = AuthorizationProvider.instance.get();
+ logger.debug("Retrieving all posts coming from vre = " + scope);
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+ try{
+ List feeds = CassandraConnection.getInstance().getDatabookStore().getAllFeedsByVRE(scope);
+ Filters.hideSensitiveInformation(feeds, caller.getClient().getId());
+ responseBean.setResult(feeds);
+ responseBean.setSuccess(true);
+ }catch(Exception e){
+ logger.error("Unable to retrieve feeds for vre = " + scope, e);
+ status = Status.INTERNAL_SERVER_ERROR;
+ responseBean.setMessage(e.toString());
+ responseBean.setSuccess(false);
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-posts-by-hashtag/")
+ @Produces({MediaType.APPLICATION_JSON})
+ @ApiOperation(value = "Retrieve posts containing the hashtag", notes="Retrieve posts containing the hashtag in the context bound to the gcube-token",
+ response=ResponseBean.class, nickname="get-posts-by-hashtag")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successfull retrieved posts, they are reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getPostsByHashTags(
+ @QueryParam("hashtag")
+ @NotNull(message="hashtag cannot be missing")
+ @ApiParam(name="hashtag",
+ required=true, allowMultiple=false, value="The hashtag to be contained within the posts")
+ String hashtag) throws ValidationException {
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+ logger.info("User " + username + " has requested posts containing hashtag " + hashtag + " in context " + context);
+ try{
+ DatabookStore datastore = CassandraConnection.getInstance().getDatabookStore();
+ List feeds = datastore.getVREFeedsByHashtag(context, hashtag);
+ Filters.hideSensitiveInformation(feeds, caller.getClient().getId());
+ responseBean.setResult(feeds);
+ responseBean.setSuccess(true);
+
+ }catch(Exception e){
+ logger.error("Failed to retrieve hashtags", e);
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-id-liked-posts/")
+ @Produces({MediaType.APPLICATION_JSON})
+ @ApiOperation(value = "Retrieve ids (UUID) of the liked by the user", notes="Retrieve ids (UUID) of the liked by the user in the context bound to the gcube-token",
+ response=ResponseBean.class, nickname="get-id-liked-posts")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successfull retrieved ids, they are reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getAllLikedPostIdsByUser() {
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ logger.debug("Retrieving all liked feeds for user with id " + username + " in context " + context);
+
+ List retrievedLikedFeedsIds = null;
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+ try{
+
+ DatabookStore datastore = CassandraConnection.getInstance().getDatabookStore();
+ retrievedLikedFeedsIds = datastore.getAllLikedFeedIdsByUser(username);
+ Filters.filterFeedsPerContextById(retrievedLikedFeedsIds, context);
+ responseBean.setResult(retrievedLikedFeedsIds);
+ responseBean.setSuccess(true);
+ logger.debug("Ids of liked feeds by " + username + " retrieved");
+ }catch(Exception e){
+ logger.error("Unable to read such ids of liked feeds.", e);
+ responseBean.setMessage(e.getMessage());
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-liked-posts/")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Retrieve posts liked by the user", notes="Retrieve posts liked by the user (up to a given quantity) in the context bound to the gcube-token",
+ response=ResponseBean.class, nickname="get-liked-posts")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successfull retrieved posts. They are reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getAllLikedPostsByUser(
+ @DefaultValue("10")
+ @QueryParam("limit")
+ @Min(message="limit cannot be negative", value = 0)
+ @ApiParam(name="limit",
+ required=false, allowMultiple=false, value="The maximum number of posts to be retrieved")
+ int limit) throws ValidationException{
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String context = ScopeProvider.instance.get();
+ List retrievedLikedFeeds = null;
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ try{
+ logger.debug("Retrieving " + limit + " liked feeds for user with id " + username + " in context " + context);
+ retrievedLikedFeeds = CassandraConnection.getInstance().getDatabookStore().getAllLikedFeedsByUser(username, limit);
+ Filters.filterFeedsPerContext(retrievedLikedFeeds, context);
+ Filters.hideSensitiveInformation(retrievedLikedFeeds, caller.getClient().getId());
+ responseBean.setResult(retrievedLikedFeeds);
+ responseBean.setSuccess(true);
+ logger.debug("Liked feeds by " + username + " retrieved");
+ }catch(Exception e){
+ logger.error("Unable to read such liked feeds.", e);
+ responseBean.setMessage(e.getMessage());
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+ return Response.status(status).entity(responseBean).build();
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Tokens.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Tokens.java
new file mode 100644
index 0000000..ed721c6
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Tokens.java
@@ -0,0 +1,91 @@
+package org.gcube.portal.social.networking.ws.methods.v2;
+
+import static org.gcube.common.authorization.client.Constants.authorizationService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+
+import javax.validation.Valid;
+import javax.validation.ValidationException;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.databook.shared.ApplicationProfile;
+import org.gcube.portal.social.networking.swagger.config.Bootstrap;
+import org.gcube.portal.social.networking.swagger.config.SwaggerConstants;
+import org.gcube.portal.social.networking.ws.inputs.ApplicationId;
+import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.gcube.portal.social.networking.ws.utils.SocialUtils;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * REST interface for the social networking library (tokens).
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("2/tokens")
+@Api(value=SwaggerConstants.TOKENS, authorizations={@Authorization(value = Bootstrap.GCUBE_TOKEN_IN_QUERY_DEF), @Authorization(value = Bootstrap.GCUBE_TOKEN_IN_HEADER_DEF)})
+public class Tokens {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Tokens.class);
+
+ @POST
+ @Path("generate-application-token/")
+ @Produces(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Generate an application token", notes="Generate an application token for the application with id app_id",
+ response=ResponseBean.class, nickname="generate-application-token")
+ @ApiResponses(value = {
+ @ApiResponse(code = 201, message = "Successful creation of the token, reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 403, message = "There is no application profile with such id", response=ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response generateApplicationToken(
+ @NotNull(message="Missing input parameter")
+ @Valid
+ @ApiParam(name="input", required=true, allowMultiple=false, value="The bean containing the app_id field")
+ ApplicationId input) throws ValidationException{
+
+ logger.debug("Incoming request for app token generation.");
+ String context = ScopeProvider.instance.get();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.CREATED;
+ String appId = input.getAppId();
+ try {
+ // check if the token actually matches an application profile
+ ApplicationProfile appProfile = SocialUtils.getProfileFromInfrastrucure(appId, context);
+ if(appProfile == null){
+ logger.error("The given id doesn't belong to an application!!!");
+ responseBean.setSuccess(false);
+ responseBean.setMessage(ErrorMessages.NOT_APP_ID);
+ status = Status.FORBIDDEN;
+ return Response.status(status).entity(responseBean).build();
+ }
+ logger.info("Generating token for the application with id " + appId);
+ // each token is related to an identifier and the context
+ String appToken = authorizationService().generateExternalServiceToken(appId);
+ responseBean.setSuccess(true);
+ responseBean.setResult(appToken);
+ } catch (Exception e) {
+ logger.error("Unable to generate token for app " + appId + " and scope " + context);
+ status = Status.INTERNAL_SERVER_ERROR;
+ responseBean.setSuccess(false);
+ responseBean.setMessage(ErrorMessages.TOKEN_GENERATION_APP_FAILED);
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Users.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Users.java
new file mode 100644
index 0000000..7d705a7
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/Users.java
@@ -0,0 +1,577 @@
+package org.gcube.portal.social.networking.ws.methods.v2;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.validation.ValidationException;
+import javax.validation.constraints.NotNull;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.common.scope.impl.ScopeBean;
+import org.gcube.common.scope.impl.ScopeBean.Type;
+import org.gcube.portal.social.networking.caches.UsersCache;
+import org.gcube.portal.social.networking.liferay.ws.GroupManagerWSBuilder;
+import org.gcube.portal.social.networking.liferay.ws.RoleManagerWSBuilder;
+import org.gcube.portal.social.networking.liferay.ws.UserManagerWSBuilder;
+import org.gcube.portal.social.networking.swagger.config.Bootstrap;
+import org.gcube.portal.social.networking.swagger.config.SwaggerConstants;
+import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
+import org.gcube.portal.social.networking.ws.utils.ErrorMessages;
+import org.gcube.portal.social.networking.ws.utils.TokensUtils;
+import org.gcube.portal.social.networking.ws.utils.UserProfileExtendedWithVerifiedEmail;
+import org.gcube.vomanagement.usermanagement.GroupManager;
+import org.gcube.vomanagement.usermanagement.RoleManager;
+import org.gcube.vomanagement.usermanagement.UserManager;
+import org.gcube.vomanagement.usermanagement.model.GCubeRole;
+import org.gcube.vomanagement.usermanagement.model.GCubeUser;
+import org.slf4j.LoggerFactory;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+
+/**
+ * REST interface for the social networking library (users).
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("2/users")
+@Api(value=SwaggerConstants.USERS, authorizations={@Authorization(value = Bootstrap.GCUBE_TOKEN_IN_QUERY_DEF), @Authorization(value = Bootstrap.GCUBE_TOKEN_IN_HEADER_DEF)})
+public class Users {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Users.class);
+ private static final String NOT_USER_TOKEN_CONTEXT_USED = "User's information can only be retrieved through a user token (not qualified)";
+ private static final List GLOBAL_ROLES_ALLOWED_BY_LOCAL_CALL_METHOD = Arrays.asList("DataMiner-Manager","Quota-Manager");
+
+ @GET
+ @Path("get-custom-attribute/")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Read a user's custom attribute", notes="Read a user's custom attribute. The user is the one owning the token",
+ response=ResponseBean.class, nickname="get-custom-attribute")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "Successful read of the attribute, reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 404, message = "Such an attribute doesn't exist", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response readCustomAttr(
+ @QueryParam("attribute")
+ @NotNull(message="attribute name is missing")
+ @ApiParam(name="attribute", required=true, allowMultiple=false, value="The key of the attribute to be read")
+ String attributeKey
+ ) throws ValidationException {
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+ if(!TokensUtils.isUserTokenDefault(caller)){
+ status = Status.FORBIDDEN;
+ responseBean.setMessage(NOT_USER_TOKEN_CONTEXT_USED);
+ logger.warn("Trying to access users method via a token different than USER is not allowed");
+ }else{
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ try{
+
+ GCubeUser user = userManager.getUserByUsername(username);
+ String toReturn = (String)userManager.readCustomAttr(user.getUserId(), attributeKey);
+ responseBean.setSuccess(true);
+ responseBean.setResult(toReturn);
+
+ }catch(Exception e){
+
+ logger.error("Unable to retrieve attribute for user.", e);
+ responseBean.setMessage(e.toString());
+ responseBean.setSuccess(false);
+ status = Status.NOT_FOUND;
+
+ }
+ }
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ // @PUT
+ // @Path("update-custom-attribute")
+ // @Consumes(MediaType.APPLICATION_JSON)
+ // @Produces(MediaType.APPLICATION_JSON)
+ // @ApiOperation(value = "Update a user's custom attribute", notes="Update a user's custom attribute. The user is the one owning the token",
+ // response=ResponseBean.class, nickname="update-custom-attribute")
+ // @ApiResponses(value = {
+ // @ApiResponse(code = 200, message = "Successful update of the attribute, the new value is reported in the 'result' field of the returned object", response = ResponseBean.class),
+ // @ApiResponse(code = 400, message = "Key or value for the new attribute missing", response=ResponseBean.class),
+ // @ApiResponse(code = 500, message = ErrorMessages.errorMessageApiResult, response=ResponseBean.class),
+ // @ApiResponse(code = 304, message = "Attribute not modified", response=ResponseBean.class)})
+ // public Response updateCustomAttr(
+ // @NotNull(message="input is missing")
+ // @ApiParam(name="input", required=true, allowMultiple=false, value="The object having an attribute key and a value key for the new value")
+ // String inputJsonObj
+ // ) throws ValidationException{
+ //
+ // Caller caller = AuthorizationProvider.instance.get();
+ // String username = caller.getClient().getId();
+ // ResponseBean responseBean = new ResponseBean();
+ // Status status = Status.OK;
+ //
+ // try{
+ //
+ // // Jackson parser
+ // ObjectMapper mapper = new ObjectMapper();
+ // JsonNode actualObj = mapper.readTree(inputJsonObj);
+ // String attributeKey = actualObj.get("attribute").asText();
+ // String newValue = actualObj.get("value").asText();
+ //
+ // if(attributeKey == null || attributeKey.isEmpty() || newValue == null){
+ //
+ // logger.error("Missing/wrong request parameters");
+ // status = Status.BAD_REQUEST;
+ // responseBean.setMessage(ErrorMessages.missingParameters);
+ // return Response.status(status).entity(responseBean).build();
+ //
+ // }
+ //
+ // GCubeUser user = userManager.getUserByUsername(username);
+ // userManager.saveCustomAttr(user.getUserId(), attributeKey, newValue);
+ // responseBean.setSuccess(true);
+ // responseBean.setResult(newValue);
+ //
+ // }catch(Exception e){
+ //
+ // logger.error("Unable to set attribute for user.", e);
+ // status = Status.NOT_MODIFIED;
+ // responseBean.setMessage(e.toString());
+ //
+ // }
+ //
+ // return Response.status(status).entity(responseBean).build();
+ // }
+
+ @GET
+ @Path("get-fullname")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Read the user's fullname", notes="Read the user's fullname. The user is the one owning the token",
+ response=ResponseBean.class, nickname="get-fullname")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "The user's fullname is reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getUserFullname(){
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String fullName = null;
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ if(!TokensUtils.isUserTokenDefault(caller)){
+ status = Status.FORBIDDEN;
+ responseBean.setMessage(NOT_USER_TOKEN_CONTEXT_USED);
+ logger.warn("Trying to access users method via a token different than USER is not allowed");
+ }else{
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ try{
+
+ GCubeUser user = userManager.getUserByUsername(username);
+ fullName = user.getFullname();
+ logger.info("Found fullname " + fullName + " for user " + username);
+ responseBean.setResult(fullName);
+ responseBean.setSuccess(true);
+
+ }catch(Exception e){
+
+ logger.error("Unable to retrieve attribute for user.", e);
+ status = Status.INTERNAL_SERVER_ERROR;
+
+ }
+ }
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-email")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Read the user's email address", notes="Read the user's email address. The user is the one owning the token",
+ response=ResponseBean.class, nickname="get-email")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "The user's email value is reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getUserEmail(){
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String email = null;
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+ if(!TokensUtils.isUserTokenDefault(caller)){
+ status = Status.FORBIDDEN;
+ responseBean.setMessage(NOT_USER_TOKEN_CONTEXT_USED);
+ logger.warn("Trying to access users method via a token different than USER is not allowed");
+ }else{
+ try{
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ GCubeUser user = userManager.getUserByUsername(username);
+ email = user.getEmail();
+ logger.info("Found email " + email + " for user " + username);
+ responseBean.setResult(email);
+ responseBean.setSuccess(true);
+
+ }catch(Exception e){
+
+ logger.error("Unable to retrieve attribute for user.", e);
+ status = Status.INTERNAL_SERVER_ERROR;
+
+ }
+ }
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-profile")
+ @Produces(MediaType.APPLICATION_JSON)
+ @ApiOperation(value = "Read the user's profile", notes="Read the user's profile. The user is the one owning the token",
+ response=ResponseBean.class, nickname="get-profile")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "The user's profile is reported in the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ public Response getUserProfile(){
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ GCubeUser user = null;
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ if(!TokensUtils.isUserTokenDefault(caller)){
+ status = Status.FORBIDDEN;
+ responseBean.setMessage(NOT_USER_TOKEN_CONTEXT_USED);
+ logger.warn("Trying to access users method via a token different than USER is not allowed");
+ }else{
+ try{
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ user = userManager.getUserByUsername(username);
+ responseBean.setResult(user);
+ responseBean.setSuccess(true);
+ }catch(Exception e){
+ logger.error("Unable to retrieve user's profile", e);
+ responseBean.setMessage(e.getMessage());
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+ }
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ private static final Function GCUBE_TO_EXTENDED_PROFILE_MAP_WITH_VERIFIED_EMAIL
+ = new Function() {
+
+ @Override
+ public UserProfileExtendedWithVerifiedEmail apply(GCubeUser t) {
+
+ if(t == null)
+ return null;
+
+ UserProfileExtendedWithVerifiedEmail profile = new UserProfileExtendedWithVerifiedEmail(t.getUsername(), null, t.getUserAvatarURL(), t.getFullname());
+ profile.setEmail(t.getEmail());
+ profile.setFirstName(t.getFirstName());
+ profile.setJobTitle(t.getJobTitle());
+ profile.setLastName(t.getLastName());
+ profile.setLocationIndustry(t.getLocation_industry());
+ profile.setMale(t.isMale());
+ profile.setMiddleName(t.getMiddleName());
+ profile.setVerifiedEmail(true);
+ return profile;
+ }
+ };
+
+ @GET
+ @Path("get-oauth-profile")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getUserOAuthProfile(){
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ String scope = ScopeProvider.instance.get();
+ GCubeUser user = null;
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+ UserProfileExtendedWithVerifiedEmail userWithEmailVerified = null;
+ if(! (TokensUtils.isUserTokenDefault(caller) || TokensUtils.isUserTokenQualified(caller))){
+ status = Status.FORBIDDEN;
+ responseBean.setMessage(NOT_USER_TOKEN_CONTEXT_USED);
+ logger.warn("Trying to access users method via a token different than USER is not allowed");
+ }else{
+ try{
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ RoleManager roleManager = RoleManagerWSBuilder.getInstance().getRoleManager();
+ GroupManager groupManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+ user = userManager.getUserByUsername(username);
+ userWithEmailVerified = GCUBE_TO_EXTENDED_PROFILE_MAP_WITH_VERIFIED_EMAIL.apply(user);
+ List roles = roleManager.listRolesByUserAndGroup(user.getUserId(), groupManager.getGroupIdFromInfrastructureScope(scope));
+ List rolesNames = new ArrayList();
+ for (GCubeRole gCubeRole : roles) {
+ rolesNames.add(gCubeRole.getRoleName());
+ }
+ userWithEmailVerified.setRoles(rolesNames);
+
+ //responseBean.setResult(userWithEmailVerified);
+ responseBean.setSuccess(true);
+ }catch(Exception e){
+ logger.error("Unable to retrieve user's profile", e);
+ responseBean.setMessage(e.getMessage());
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+ }
+ logger.debug("returning: "+userWithEmailVerified.toString());
+ return Response.status(status).entity(userWithEmailVerified).build();
+ }
+
+
+ @GET
+ @Path("get-all-usernames")
+ @ApiOperation(value = "Get the list of usernames belonging to a given context", notes="Retrieve the list of usernames for the user belonging to the context linked to the provided token.",
+ response=ResponseBean.class, nickname="get-all-usernames")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "The list of usernames is put into the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getAllUserNames(){
+
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ List usernames = new ArrayList();
+ try{
+
+ GroupManager groupManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ long groupId = groupManager.getGroupIdFromInfrastructureScope(ScopeProvider.instance.get());
+
+ // first retrieve ids
+ List userIds = userManager.getUserIdsByGroup(groupId);
+
+ // check info in cache when available
+ UsersCache cache = UsersCache.getSingleton();
+
+ for (Long userId : userIds) {
+ if(cache.getUser(userId) == null){
+ GCubeUser user = userManager.getUserById(userId);
+ if(user != null){
+ usernames.add(user.getUsername());
+ cache.pushEntry(userId, user);
+ }
+ }else
+ usernames.add(cache.getUser(userId).getUsername());
+ }
+
+ responseBean.setResult(usernames);
+ responseBean.setSuccess(true);
+ }catch(Exception e){
+ logger.error("Unable to retrieve user's usernames", e);
+ responseBean.setMessage(e.getMessage());
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-all-fullnames-and-usernames")
+ @ApiOperation(value = "Get the map of couples username/fullname of the users belonging to a given context", notes="Get the map of couples username/fullname of the users belonging to the context linked to the provided token.",
+ response=ResponseBean.class, nickname="get-all-fullnames-and-usernames")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "The map is put into the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getFullnamesAndUsernames(){
+
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ Map usernamesAndFullnames = new HashMap();
+ try{
+
+ GroupManager groupManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ long groupId = groupManager.getGroupIdFromInfrastructureScope(ScopeProvider.instance.get());
+
+ // first retrieve ids
+ List userIds = userManager.getUserIdsByGroup(groupId);
+
+ // check info in cache when available
+ UsersCache cache = UsersCache.getSingleton();
+
+ for (Long userId : userIds) {
+ if(cache.getUser(userId) == null){
+ GCubeUser user = userManager.getUserById(userId);
+ if(user != null){
+ usernamesAndFullnames.put(user.getUsername(), user.getFullname());
+ cache.pushEntry(userId, user);
+ }
+ }else
+ usernamesAndFullnames.put(cache.getUser(userId).getUsername(), cache.getUser(userId).getFullname());
+ }
+
+ responseBean.setResult(usernamesAndFullnames);
+ responseBean.setSuccess(true);
+ }catch(Exception e){
+ logger.error("Unable to retrieve user's usernames", e);
+ responseBean.setMessage(e.getMessage());
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-usernames-by-global-role")
+ @ApiOperation(value = "Get the list of users having a given global-role", notes="Get the list of users having a given global-role, e.g. 'Administrator'.",
+ response=ResponseBean.class, nickname="get-usernames-by-global-role")
+ @ApiResponses(value = {
+ @ApiResponse(code = 200, message = "The list is put into the 'result' field of the returned object", response = ResponseBean.class),
+ @ApiResponse(code = 500, message = ErrorMessages.ERROR_IN_API_RESULT, response=ResponseBean.class)})
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getUsernamesByGlobalRole(
+ @ApiParam(value = "role-name: the name of the role to be checked (e.g. Administrator)", required = true)
+ @QueryParam("role-name") String roleName){
+
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ // this method can only be called from IS scope (except for roles in GLOBAL_ROLES_ALLOWED_BY_LOCAL)
+ ScopeBean scopeInfo = new ScopeBean(ScopeProvider.instance.get());
+
+ if(!scopeInfo.is(Type.INFRASTRUCTURE)){
+ status = Status.BAD_REQUEST;
+ responseBean.setMessage("This method can only be called with an infrastructure token");
+ }else{
+
+ List usernames = new ArrayList();
+ try{
+ RoleManager roleManager = RoleManagerWSBuilder.getInstance().getRoleManager();
+ long globalRoleId = roleManager.getRoleIdByName(roleName);
+ if(globalRoleId > 0){
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ List users = userManager.listUsersByGlobalRole(globalRoleId);
+ if(users != null){
+ for (GCubeUser gCubeUser : users) {
+ usernames.add(gCubeUser.getUsername());
+ }
+ }
+ responseBean.setResult(usernames);
+ responseBean.setSuccess(true);
+ }else{
+ responseBean.setMessage("No global role exists whit such a name");
+ status = Status.BAD_REQUEST;
+ }
+ }catch(Exception e){
+ logger.error("Unable to retrieve user's usernames", e);
+ responseBean.setMessage(e.getMessage());
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+ }
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("get-usernames-by-role")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getUsernamesByRole(
+ @QueryParam("role-name") String roleName){
+
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+ String context = ScopeProvider.instance.get();
+ List usernames = new ArrayList();
+ try{
+ GroupManager groupManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+ RoleManager roleManager = RoleManagerWSBuilder.getInstance().getRoleManager();
+ long roleId = roleManager.getRoleIdByName(roleName);
+ if(roleId > 0){
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ List users = null;
+ long groupId = groupManager.getGroupIdFromInfrastructureScope(context);
+ // first check if for any reason this is a global role, then (if result is null or exception arises) check for site role
+ // Global role's users are retrieved much faster
+ try{
+ if(GLOBAL_ROLES_ALLOWED_BY_LOCAL_CALL_METHOD.contains(roleName)){
+ // TODO inconsistent value can be returned
+ users = userManager.listUsersByGlobalRole(roleId);
+ }
+ }catch(Exception globalExp){
+ logger.warn("Failed while checking for global role... trying with local one", globalExp);
+ }
+
+ if(users == null || users.isEmpty()){
+ logger.debug("User list is still null/empty, checking for local information");
+ users = userManager.listUsersByGroupAndRole(groupId, roleId);
+ }
+
+ if(users != null){
+ for (GCubeUser gCubeUser : users) {
+ usernames.add(gCubeUser.getUsername());
+ }
+ }
+ responseBean.setResult(usernames);
+ responseBean.setSuccess(true);
+ }else{
+ responseBean.setMessage("No role exists whit such a name");
+ status = Status.BAD_REQUEST;
+ }
+ }catch(Exception e){
+ logger.error("Unable to retrieve user's usernames", e);
+ responseBean.setMessage(e.getMessage());
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+ @GET
+ @Path("user-exists")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response existUser(@QueryParam("username") String username){
+
+ ResponseBean responseBean = new ResponseBean();
+ String messageOnError = "This method can be invoked only by using an application token bound to the root context";
+ Status status = Status.BAD_REQUEST;
+ responseBean.setMessage(messageOnError);
+ responseBean.setSuccess(false);
+ Caller caller = AuthorizationProvider.instance.get();
+
+ if(!TokensUtils.isApplicationToken(caller))
+ return Response.status(status).entity(responseBean).build();
+
+ ScopeBean scopeInfo = new ScopeBean(ScopeProvider.instance.get());
+
+ if(!scopeInfo.is(Type.INFRASTRUCTURE))
+ return Response.status(status).entity(responseBean).build();
+
+ try{
+
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ GCubeUser user = userManager.getUserByUsername(username);
+ responseBean.setSuccess(true);
+ responseBean.setMessage(null);
+ responseBean.setResult(user != null);
+ status = Status.OK;
+
+ }catch(Exception e){
+ logger.error("Unable to retrieve such information", e);
+ responseBean.setMessage(e.getMessage());
+ status = Status.INTERNAL_SERVER_ERROR;
+ }
+
+ return Response.status(status).entity(responseBean).build();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/VREs.java b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/VREs.java
new file mode 100644
index 0000000..6efa424
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/methods/v2/VREs.java
@@ -0,0 +1,116 @@
+package org.gcube.portal.social.networking.ws.methods.v2;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.Authorization;
+
+import java.util.List;
+
+import javax.validation.ValidationException;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.gcube.common.authorization.library.provider.AuthorizationProvider;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.portal.social.networking.liferay.ws.GroupManagerWSBuilder;
+import org.gcube.portal.social.networking.liferay.ws.RoleManagerWSBuilder;
+import org.gcube.portal.social.networking.liferay.ws.UserManagerWSBuilder;
+import org.gcube.portal.social.networking.swagger.config.Bootstrap;
+import org.gcube.portal.social.networking.swagger.config.SwaggerConstants;
+import org.gcube.portal.social.networking.ws.outputs.ResponseBean;
+import org.gcube.portal.social.networking.ws.utils.TokensUtils;
+import org.gcube.vomanagement.usermanagement.GroupManager;
+import org.gcube.vomanagement.usermanagement.RoleManager;
+import org.gcube.vomanagement.usermanagement.UserManager;
+import org.gcube.vomanagement.usermanagement.model.GCubeGroup;
+import org.gcube.vomanagement.usermanagement.model.GCubeUser;
+import org.gcube.vomanagement.usermanagement.model.GatewayRolesNames;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * REST interface for the social networking library (vres).
+ * @author Costantino Perciante at ISTI-CNR
+ */
+@Path("2/vres")
+@Api(value=SwaggerConstants.VRES, authorizations={@Authorization(value = Bootstrap.GCUBE_TOKEN_IN_QUERY_DEF), @Authorization(value = Bootstrap.GCUBE_TOKEN_IN_HEADER_DEF)})
+public class VREs {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Users.class);
+
+ @SuppressWarnings("unchecked")
+ @GET
+ @Path("get-my-vres/")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getMyVres(
+ @DefaultValue("false") @QueryParam("getManagers") boolean getManagers
+ ) throws ValidationException {
+
+ Caller caller = AuthorizationProvider.instance.get();
+ String username = caller.getClient().getId();
+ ResponseBean responseBean = new ResponseBean();
+ Status status = Status.OK;
+
+ if(!TokensUtils.isUserToken(caller)){
+ status = Status.FORBIDDEN;
+ responseBean.setMessage("This method can only be invoked with a user token!");
+ logger.warn("Trying to access get-my-vres method via a token different than USER is not allowed");
+ }else{
+ GroupManager gmManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+ UserManager userManager = UserManagerWSBuilder.getInstance().getUserManager();
+ RoleManager roleManager = RoleManagerWSBuilder.getInstance().getRoleManager();
+ try{
+
+ GCubeUser user = userManager.getUserByUsername(username);
+ List vres = gmManager.listVresByUser(user.getUserId());
+ JSONArray toReturn = new JSONArray();
+
+ for (GCubeGroup group : vres) {
+
+ // # ticket 9333
+ JSONObject obj = new JSONObject();
+ obj.put("name", group.getGroupName());
+ obj.put("context", gmManager.getInfrastructureScope(group.getGroupId()));
+ obj.put("description", group.getDescription());
+ //obj.put("thumbnail_url", ...); // TODO
+ JSONArray managers = new JSONArray();
+
+ if(getManagers){
+ List vreManagers = userManager.listUsersByGroupAndRole(group.getGroupId(),
+ roleManager.getRoleIdByName(GatewayRolesNames.VRE_MANAGER.getRoleName()));
+
+ for (GCubeUser vreManager : vreManagers) {
+ JSONObject manager = new JSONObject();
+ manager.put("username", vreManager.getUsername());
+ manager.put("fullname", vreManager.getFullname());
+ managers.add(manager);
+ }
+
+ obj.put("managers", managers);
+ }
+ toReturn.add(obj);
+ }
+
+ responseBean.setSuccess(true);
+ responseBean.setResult(toReturn);
+
+ }catch(Exception e){
+
+ logger.error("Unable to retrieve vres for user.", e);
+ responseBean.setMessage(e.toString());
+ responseBean.setSuccess(false);
+ status = Status.INTERNAL_SERVER_ERROR;
+
+ }
+ }
+ return Response.status(status).entity(responseBean).build();
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/outputs/ResponseBean.java b/src/main/java/org/gcube/portal/social/networking/ws/outputs/ResponseBean.java
new file mode 100644
index 0000000..7a89c25
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/outputs/ResponseBean.java
@@ -0,0 +1,79 @@
+package org.gcube.portal.social.networking.ws.outputs;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * Response bean
+ * @author Costantino Perciante at ISTI-CNR
+ * (costantino.perciante@isti.cnr.it)
+ *
+ */
+@ApiModel(description="A response object", value="Response")
+public class ResponseBean implements Serializable {
+
+ private static final long serialVersionUID = -2725238162673879658L;
+ @ApiModelProperty(value="The result of the request: true if it succeeded, false otherwise")
+ private boolean success;
+
+ @ApiModelProperty(value="An error message if something wrong happened, null/empty otherwise")
+ private String message;
+
+ @ApiModelProperty(value="The result object of the request")
+ private Object result;
+
+ public ResponseBean() {
+ super();
+ }
+
+ /**
+ * @param success
+ * @param message
+ * @param result
+ * @param help
+ */
+ public ResponseBean(boolean success, String message, Object result) {
+ super();
+ this.success = success;
+ this.message = message;
+ this.result = result;
+ }
+
+
+ public boolean isSuccess() {
+ return success;
+ }
+
+
+ public void setSuccess(boolean success) {
+ this.success = success;
+ }
+
+
+ public String getMessage() {
+ return message;
+ }
+
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+
+ public Object getResult() {
+ return result;
+ }
+
+
+ public void setResult(Object result) {
+ this.result = result;
+ }
+
+ @Override
+ public String toString() {
+ return "ResponseBean [success=" + success
+ + ", message=" + message + ", result=" + result + "]";
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/providers/CustomObjectMapper.java b/src/main/java/org/gcube/portal/social/networking/ws/providers/CustomObjectMapper.java
new file mode 100644
index 0000000..b5b3554
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/providers/CustomObjectMapper.java
@@ -0,0 +1,37 @@
+package org.gcube.portal.social.networking.ws.providers;
+
+import javax.ws.rs.ext.ContextResolver;
+import javax.ws.rs.ext.Provider;
+
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.PropertyNamingStrategy;
+import com.fasterxml.jackson.databind.SerializationFeature;
+
+@Provider
+/**
+ * Custom mapper with property CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES to perform serialization/deserialization
+ * with snake case over camel case for json beans.
+ * TODO check https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations for applying such transformation
+ * only to some classes.
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class CustomObjectMapper implements ContextResolver {
+
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CustomObjectMapper.class);
+ private final ObjectMapper mapper;
+
+ public CustomObjectMapper() {
+ logger.debug("new ObjectMapperResolver()");
+ mapper = new ObjectMapper();
+ mapper.enable(SerializationFeature.INDENT_OUTPUT);
+ mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
+ }
+
+ @Override
+ public ObjectMapper getContext(Class> type) {
+ logger.debug("ObjectMapperResolver.getContext(...) invoked");
+ return mapper;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/providers/JobStatusTypeDeserializer.java b/src/main/java/org/gcube/portal/social/networking/ws/providers/JobStatusTypeDeserializer.java
new file mode 100644
index 0000000..fc3b5c1
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/providers/JobStatusTypeDeserializer.java
@@ -0,0 +1,46 @@
+package org.gcube.portal.social.networking.ws.providers;
+
+import java.io.IOException;
+
+import org.gcube.portal.databook.shared.JobStatusType;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+
+/**
+ * Deserializer used to map a string representing the status in this JobNotificationBean to the JobStatusType enum.
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class JobStatusTypeDeserializer extends JsonDeserializer{
+
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JobStatusTypeDeserializer.class);
+
+ @Override
+ public JobStatusType deserialize(JsonParser p,
+ DeserializationContext ctxt) throws IOException,
+ JsonProcessingException {
+
+ logger.debug("Status deserializer called");
+
+ String status = p.getText();
+ JobStatusType toReturn = null;
+
+ logger.debug("Status coming from json object is " + status);
+
+ JobStatusType[] values = JobStatusType.values();
+
+ for (JobStatusType jobStatusType : values) {
+ if(jobStatusType.toString().toLowerCase().contains(status.toLowerCase())){
+ toReturn = jobStatusType;
+ break;
+ }
+ }
+
+ logger.debug("JOB STATUS deserialized as " + toReturn);
+ return toReturn;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/utils/CassandraConnection.java b/src/main/java/org/gcube/portal/social/networking/ws/utils/CassandraConnection.java
new file mode 100644
index 0000000..1a7fee3
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/utils/CassandraConnection.java
@@ -0,0 +1,50 @@
+package org.gcube.portal.social.networking.ws.utils;
+
+import org.gcube.portal.databook.server.DBCassandraAstyanaxImpl;
+import org.gcube.portal.databook.server.DatabookStore;
+import org.gcube.smartgears.ContextProvider;
+import org.gcube.smartgears.context.application.ApplicationContext;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Cassandra connection class.
+ * @author Costantino Perciante at ISTI-CNR
+ */
+public class CassandraConnection {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(CassandraConnection.class);
+
+ // databook store (singleton)
+ private static DatabookStore store;
+
+ private static CassandraConnection singleton = new CassandraConnection();
+
+ private CassandraConnection(){
+ ApplicationContext ctx = ContextProvider.get(); // get this info from SmartGears
+ logger.info("Getting connection to cassandra");
+ store = new DBCassandraAstyanaxImpl(ctx.container().configuration().infrastructure());
+ logger.info("Connection to cassandra created");
+ }
+
+ /**
+ * Returns the object to query the cassandra cluster.
+ * @return connection pool to cassandra cluster
+ */
+ public DatabookStore getDatabookStore(){
+
+ return store;
+
+ }
+
+ /**
+ * Get the instance
+ * @return
+ */
+ public static CassandraConnection getInstance(){
+
+ return singleton;
+
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/utils/ElasticSearchConnection.java b/src/main/java/org/gcube/portal/social/networking/ws/utils/ElasticSearchConnection.java
new file mode 100644
index 0000000..32939cc
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/utils/ElasticSearchConnection.java
@@ -0,0 +1,52 @@
+package org.gcube.portal.social.networking.ws.utils;
+
+import org.gcube.smartgears.ContextProvider;
+import org.gcube.smartgears.context.application.ApplicationContext;
+import org.gcube.socialnetworking.social_data_search_client.ElasticSearchClient;
+import org.gcube.socialnetworking.social_data_search_client.ElasticSearchClientImpl;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The class discovers and offer connections to the elastic search cluster.
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class ElasticSearchConnection {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ElasticSearchConnection.class);
+
+ // databook store (singleton)
+ private ElasticSearchClient es;
+
+ // singleton
+ private static ElasticSearchConnection singleton = new ElasticSearchConnection();
+
+ private ElasticSearchConnection(){
+ try {
+ ApplicationContext ctx = ContextProvider.get(); // get this info from SmartGears
+ logger.info("Creating connection to Elasticsearch");
+ es = new ElasticSearchClientImpl(ctx.container().configuration().infrastructure());
+ logger.info("Elasticsearch connection created");
+ } catch (Exception e) {
+ logger.error("Failed to connect to elasticsearch", e);
+ }
+ }
+
+ public static ElasticSearchConnection getSingleton(){
+
+ return singleton;
+
+ }
+
+ /**
+ * Returns the object to connect to cassandra cluster.
+ * @return connection pool to cassandra cluster
+ * @throws Exception
+ */
+ public ElasticSearchClient getElasticSearchClient(){
+
+ return es;
+
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/utils/ErrorMessages.java b/src/main/java/org/gcube/portal/social/networking/ws/utils/ErrorMessages.java
new file mode 100644
index 0000000..d1a8852
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/utils/ErrorMessages.java
@@ -0,0 +1,20 @@
+package org.gcube.portal.social.networking.ws.utils;
+
+
+/**
+ * Class that contains error messages to be returned in the HTTP responses.
+ * @author Costantino Perciante at ISTI-CNR
+ */
+public class ErrorMessages {
+
+ public static final String MISSING_TOKEN = "Missing token.";
+ public static final String MISSING_PARAMETERS = "Missing request parameters.";
+ public static final String INVALID_TOKEN = "Invalid token.";
+ public static final String TOKEN_GENERATION_APP_FAILED = "Token generation failed.";
+ public static final String NOT_APP_TOKEN = "Invalid token: not belonging to an application.";
+ public static final String NOT_APP_ID = "Invalid application id: it doesn't belong to an application.";
+ public static final String NO_APP_PROFILE_FOUND = "There is no application profile for this app id/scope.";
+ public static final String BAD_REQUEST = "Please check the parameter you passed, it seems a bad request";
+ public static final String ERROR_IN_API_RESULT = "The error is reported into the 'message' field of the returned object";
+ public static final String POST_OUTSIDE_VRE = "A post cannot be written into a context that is not a VRE";
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/utils/Filters.java b/src/main/java/org/gcube/portal/social/networking/ws/utils/Filters.java
new file mode 100644
index 0000000..89bc663
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/utils/Filters.java
@@ -0,0 +1,170 @@
+package org.gcube.portal.social.networking.ws.utils;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.gcube.portal.databook.server.DatabookStore;
+import org.gcube.portal.databook.shared.Comment;
+import org.gcube.portal.databook.shared.EnhancedFeed;
+import org.gcube.portal.databook.shared.Feed;
+import org.gcube.portal.social.networking.liferay.ws.GroupManagerWSBuilder;
+import org.gcube.vomanagement.usermanagement.GroupManager;
+import org.gcube.vomanagement.usermanagement.exception.GroupRetrievalFault;
+import org.gcube.vomanagement.usermanagement.exception.UserManagementSystemException;
+import org.gcube.vomanagement.usermanagement.model.GCubeGroup;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Filters to apply to feeds/comments etc
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class Filters {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Filters.class);
+
+ private static List getContexts(String context) throws IllegalArgumentException, UserManagementSystemException, GroupRetrievalFault{
+
+ // retrieve group information
+ GroupManager gm = GroupManagerWSBuilder.getInstance().getGroupManager();
+ GCubeGroup group = gm.getGroup(gm.getGroupIdFromInfrastructureScope(context));
+
+ List contexts = new ArrayList();
+
+ if(gm.isRootVO(group.getGroupId())){
+
+ }
+ else if(gm.isVO(group.getGroupId())){
+
+ List vres = group.getChildren();
+ for (GCubeGroup gCubeGroup : vres) {
+ contexts.add(gm.getInfrastructureScope(gCubeGroup.getGroupId()));
+ }
+
+ }else{
+ contexts.add(gm.getInfrastructureScope(group.getGroupId()));
+ }
+
+ return contexts;
+ }
+
+ /**
+ * Given a list of not filtered feeds, the methods remove feeds unaccessible in this scope.
+ * If the initial context is the root: all feeds are returned;
+ * If the initial context is a VO: feeds for vres within the vo are returned;
+ * If the initial context is a vre: feeds of the vre are returned;
+ * @param feedsIds
+ * @param context
+ * @throws Exception
+ */
+ public static void filterFeedsPerContextById(
+ List feedsIds, String context) throws Exception {
+
+ DatabookStore datastore = CassandraConnection.getInstance().getDatabookStore();
+ List feeds = new ArrayList();
+
+ for (String feedId : feedsIds) {
+ try{
+ feeds.add(datastore.readFeed(feedId));
+ }catch(Exception e){
+ logger.error("Unable to read feed with id " + feedId, e);
+ }
+ }
+
+ // filter
+ filterFeedsPerContext(feeds, context);
+
+ // clear and convert
+ feedsIds.clear();
+ for (Feed feed : feeds) {
+ feedsIds.add(feed.getKey());
+ }
+
+ }
+
+ /**
+ * Given a list of not filtered feeds, the methods remove feeds unaccessible in this scope.
+ * If the initial context is the root: all feeds are returned;
+ * If the initial context is a VO: feeds for vres within the vo are returned;
+ * If the initial context is a vre: feeds of the vre are returned;
+ * @param retrievedLikedFeeds
+ * @param context
+ * @throws Exception
+ */
+ public static void filterFeedsPerContext(List feeds, String context) throws Exception {
+
+ List contexts = getContexts(context);
+
+ // filter
+ Iterator iterator = feeds.iterator();
+ while (iterator.hasNext()) {
+ Feed feed = (Feed) iterator.next();
+ if(!contexts.contains(feed.getVreid()))
+ iterator.remove();
+ }
+
+ }
+
+ /**
+ * Filter comments per context
+ * @param comments
+ * @param context
+ * @throws Exception
+ */
+ public static void filterCommentsPerContext(List comments, String context) throws Exception {
+
+ List contexts = getContexts(context);
+
+ // get cassandra store
+ DatabookStore datastore = CassandraConnection.getInstance().getDatabookStore();
+
+ // filter
+ Iterator iterator = comments.iterator();
+ while (iterator.hasNext()) {
+ try{
+ Comment comment = (Comment) iterator.next();
+ Feed parent = datastore.readFeed(comment.getFeedid());
+ if(!contexts.contains(parent.getVreid()))
+ iterator.remove();
+ }catch(Exception e){
+ logger.warn("Failed to analyze this comment", e);
+ iterator.remove(); // remove it anyway
+ }
+ }
+
+ }
+
+ /**
+ * Depending on the type of object provided (e.g. Feed, Comment etc), some information are removed
+ * @param comments
+ * @throws Exception
+ */
+ public static void hideSensitiveInformation(List toClear, String usernameCaller){
+
+ if(toClear == null || toClear.isEmpty() || usernameCaller == null || usernameCaller.isEmpty())
+ return;
+ else{
+
+ // for feeds
+ if(toClear.get(0).getClass().equals(Feed.class)){
+
+ for (T feed : toClear) {
+ Feed feeded = ((Feed)feed);
+ if(!usernameCaller.equals(feeded.getEntityId()))
+ feeded.setEmail(""); // remove the email field
+ }
+
+ }else if(toClear.get(0).getClass().equals(EnhancedFeed.class)){
+ for (T enhancedFeed : toClear) {
+ Feed feeded = ((EnhancedFeed)enhancedFeed).getFeed();
+ if(!usernameCaller.equals(feeded.getEntityId()))
+ feeded.setEmail(""); // remove the email field
+ }
+ }
+
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/utils/SocialUtils.java b/src/main/java/org/gcube/portal/social/networking/ws/utils/SocialUtils.java
new file mode 100644
index 0000000..741ab31
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/utils/SocialUtils.java
@@ -0,0 +1,454 @@
+package org.gcube.portal.social.networking.ws.utils;
+
+import static org.gcube.resources.discovery.icclient.ICFactory.client;
+
+import java.io.StringReader;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.UUID;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.gcube.applicationsupportlayer.social.ApplicationNotificationsManager;
+import org.gcube.applicationsupportlayer.social.NotificationsManager;
+import org.gcube.applicationsupportlayer.social.ex.ApplicationProfileNotFoundException;
+import org.gcube.applicationsupportlayer.social.shared.SocialNetworkingSite;
+import org.gcube.applicationsupportlayer.social.shared.SocialNetworkingUser;
+import org.gcube.common.authorization.library.utils.Caller;
+import org.gcube.common.resources.gcore.utils.XPathHelper;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.common.scope.impl.ScopeBean;
+import org.gcube.portal.databook.shared.ApplicationProfile;
+import org.gcube.portal.databook.shared.Feed;
+import org.gcube.portal.databook.shared.FeedType;
+import org.gcube.portal.databook.shared.PrivacyLevel;
+import org.gcube.portal.databook.shared.ex.FeedIDNotFoundException;
+import org.gcube.portal.notifications.thread.PostNotificationsThread;
+import org.gcube.portal.social.networking.caches.SocialNetworkingSiteFinder;
+import org.gcube.portal.social.networking.liferay.ws.GroupManagerWSBuilder;
+import org.gcube.portal.social.networking.liferay.ws.UserManagerWSBuilder;
+import org.gcube.resources.discovery.client.api.DiscoveryClient;
+import org.gcube.resources.discovery.client.queries.api.Query;
+import org.gcube.resources.discovery.client.queries.impl.QueryBox;
+import org.gcube.vomanagement.usermanagement.GroupManager;
+import org.gcube.vomanagement.usermanagement.UserManager;
+import org.gcube.vomanagement.usermanagement.model.GCubeUser;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+
+/**
+ * Utility class.
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class SocialUtils {
+
+ // Logger
+ private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SocialUtils.class);
+
+ public final static String NO_TEXT_FILE_SHARE = "_N0_73X7_SH4R3_";
+
+ // name of the portlet for vre notification
+ public static final String NEWS_FEED_PORTLET_CLASSNAME = "org.gcube.portlets.user.newsfeed.server.NewsServiceImpl";
+
+ /**
+ * Method used when an application needs to publish something.
+ * @param feedText
+ * @param uriParams
+ * @param previewTitle
+ * @param previewDescription
+ * @param httpImageUrl
+ * @return true upon success, false on failure
+ */
+ public static Feed shareApplicationUpdate(
+ String postText,
+ String uriParams,
+ String previewTitle,
+ String previewDescription,
+ String httpImageUrl,
+ ApplicationProfile applicationProfile,
+ Caller caller,
+ boolean notifyGroup
+ ){
+
+ String escapedFeedText = org.gcube.social_networking.socialutillibrary.Utils.escapeHtmlAndTransformUrl(postText);
+ logger.info("Escaped post text is " + escapedFeedText);
+ String scope = ScopeProvider.instance.get();
+ String appId = caller.getClient().getId();
+
+ List hashtags = org.gcube.social_networking.socialutillibrary.Utils.getHashTags(postText);
+ if (hashtags != null && !hashtags.isEmpty())
+ escapedFeedText = org.gcube.social_networking.socialutillibrary.Utils.convertHashtagsAnchorHTML(escapedFeedText, hashtags);
+
+ Feed toWrite =
+ buildFeed(
+ escapedFeedText,
+ uriParams == null ? "" : uriParams,
+ previewTitle == null ? "" : previewTitle,
+ previewDescription == null ? "" : previewDescription,
+ httpImageUrl == null ? "" : httpImageUrl,
+ applicationProfile,
+ scope);
+
+ // try to save it
+ boolean res = CassandraConnection.getInstance().getDatabookStore().saveAppFeed(toWrite);
+
+ if(res){
+ logger.info("Feed correctly written by application " + appId);
+
+ try {
+ CassandraConnection.getInstance().getDatabookStore().saveHashTags(toWrite.getKey(), scope, hashtags);
+ } catch (FeedIDNotFoundException e1) {
+ logger.error("Failed to save hashtags in Cassandra", e1);
+ }
+
+ if(notifyGroup){
+
+ logger.debug("Sending notifications for " + appId + " " + scope);
+
+ try{
+
+ String name = new ScopeBean(scope).name(); // scope such as devVRE
+
+ // retrieve group information
+ GroupManager gManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+
+ long groupId = gManager.getGroupId(name);
+ String groupName = gManager.getGroup(groupId).getGroupName();
+
+ logger.debug("Company id and name " + groupId + " " + groupName);
+
+ // build the notification manager
+ SocialNetworkingSite site = SocialNetworkingSiteFinder.getSocialNetworkingSiteFromScope(ScopeProvider.instance.get());
+ SocialNetworkingUser user = new SocialNetworkingUser(appId, "", applicationProfile.getName(), applicationProfile.getImageUrl());
+ NotificationsManager nm = new ApplicationNotificationsManager(
+ UserManagerWSBuilder.getInstance().getUserManager(),
+ site,
+ scope,
+ user,
+ NEWS_FEED_PORTLET_CLASSNAME);
+
+ // start notification thread
+ new Thread(new PostNotificationsThread(
+ UserManagerWSBuilder.getInstance().getUserManager(),
+ toWrite.getKey(),
+ toWrite.getDescription(),
+ ""+groupId,
+ nm,
+ new HashSet(hashtags),
+ new HashSet())
+ ).start();
+
+ }catch (Exception e) {
+ logger.debug("Feed succesfully created but unable to send notifications.");
+ }
+
+ }
+ return toWrite;
+ }
+ else
+ return null;
+
+ }
+
+ /**
+ * Build an ApplicationProfile Feed.
+ *
+ * @param description add a description for the update you are sharing
+ * @param uriParams the additional parameters your applicationProfile needs to open the subject of this update e.g. id=12345&type=foo
+ * @param previewTitle the title to show in the preview
+ * @param previewDescription the description to show in the preview
+ * @param previewThumbnailUrl the image url to show in the preview
+ * @return a feed instance ready to be written
+ */
+ private static Feed buildFeed(
+ String description,
+ String uriParams,
+ String previewTitle,
+ String previewDescription,
+ String previewThumbnailUrl,
+ ApplicationProfile applicationProfile,
+ String scopeApp) {
+
+ String uri = applicationProfile.getUrl();
+
+ //add the GET params if necessary
+ if (uriParams != null && uriParams.compareTo("") != 0)
+ uri += "?"+uriParams;
+
+ Feed toReturn = new Feed(
+ UUID.randomUUID().toString(),
+ FeedType.PUBLISH,
+ applicationProfile.getKey(),
+ new Date(),
+ scopeApp,
+ uri,
+ previewThumbnailUrl,
+ description,
+ PrivacyLevel.SINGLE_VRE,
+ applicationProfile.getName(),
+ "no-email",
+ applicationProfile.getImageUrl(),
+ previewTitle,
+ previewDescription,
+ "",
+ true);
+
+ return toReturn;
+ }
+
+ /**
+ * This method looks up the applicationProfile profile among the ones available in the infrastructure
+ * @param idApp as identifier of your application (as reported in the ApplicationProfile)
+ * @param scopeApp the scope of the application
+ */
+ public static ApplicationProfile getProfileFromInfrastrucure(String idApp, String scopeApp) {
+ ScopeBean scope = new ScopeBean(scopeApp);
+
+ logger.debug("Trying to fetch applicationProfile profile from the infrastructure for " + idApp + " scope: " + scope);
+
+ // set the scope of the root infrastructure
+ String rootInfrastructure = scopeApp.split("/")[1];
+ ScopeProvider.instance.set("/"+rootInfrastructure);
+
+ try {
+
+ ApplicationProfile toReturn = new ApplicationProfile();
+ Query q = new QueryBox("for $profile in collection('/db/Profiles/GenericResource')//Resource " +
+ "where $profile/Profile/SecondaryType/string() eq 'ApplicationProfile' and $profile/Profile/Body/AppId/string() " +
+ " eq '" + idApp + "'" +
+ "return $profile");
+
+
+ DiscoveryClient client = client();
+ List appProfile = client.submit(q);
+
+ if (appProfile == null || appProfile.size() == 0)
+ throw new Exception("Your applicationProfile is not registered in the infrastructure");
+
+ else {
+
+ String elem = appProfile.get(0);
+ DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ Node node = docBuilder.parse(new InputSource(new StringReader(elem))).getDocumentElement();
+ XPathHelper helper = new XPathHelper(node);
+
+ List currValue = null;
+ currValue = helper.evaluate("/Resource/Profile/Name/text()");
+ if (currValue != null && currValue.size() > 0) {
+
+ toReturn.setName(currValue.get(0));
+
+ }
+ else throw new ApplicationProfileNotFoundException("Your applicationProfile NAME was not found in the profile");
+
+ currValue = helper.evaluate("/Resource/Profile/Description/text()");
+ if (currValue != null && currValue.size() > 0) {
+
+ toReturn.setDescription(currValue.get(0));
+
+ }
+ else logger.warn("No Description exists for " + toReturn.getName());
+
+ currValue = helper.evaluate("/Resource/Profile/Body/AppId/text()");
+
+ if (currValue != null && currValue.size() > 0) {
+
+ toReturn.setKey(currValue.get(0));
+
+ }
+ else throw new ApplicationProfileNotFoundException("Your applicationProfile ID n was not found in the profile, consider adding element in ");
+
+ currValue = helper.evaluate("/Resource/Profile/Body/ThumbnailURL/text()");
+ if (currValue != null && currValue.size() > 0) {
+
+ toReturn.setImageUrl(currValue.get(0));
+
+ }
+ else throw new Exception("Your applicationProfile Image Url was not found in the profile, consider adding element in ");
+ currValue = helper.evaluate("/Resource/Profile/Body/EndPoint/Scope/text()");
+ if (currValue != null && currValue.size() > 0) {
+
+ List scopes = currValue;
+ boolean foundUrl = false;
+ for (int i = 0; i < scopes.size(); i++) {
+ if (currValue.get(i).trim().compareTo(scope.toString()) == 0) {
+ toReturn.setUrl(helper.evaluate("/Resource/Profile/Body/EndPoint/URL/text()").get(i));
+ toReturn.setScope(scope.toString());
+ foundUrl = true;
+ break;
+ }
+ }
+
+ if (! foundUrl)
+ throw new ApplicationProfileNotFoundException("Your applicationProfile URL was not found in the profile for Scope: " + scope.toString());
+ }
+
+ else throw new ApplicationProfileNotFoundException("Your applicationProfile EndPoint was not found in the profile, consider adding element in ");
+ logger.debug("Returning " + toReturn);
+ return toReturn;
+ }
+
+ } catch (Exception e) {
+
+ logger.error("Error while trying to fetch applicationProfile profile from the infrastructure", e);
+
+ } finally{
+
+ // set the scope back
+ ScopeProvider.instance.set(scopeApp);
+
+ }
+
+ return null;
+
+ }
+
+ /**
+ * Allows user to post a feed in a certain vre.
+ * @param userId
+ * @param postText
+ * @param vreId
+ * @param previewTitle
+ * @param previewDescription
+ * @param previewHost
+ * @param previewUrl
+ * @param urlThumbnail
+ * @param notifyGroup
+ * @return The written Feed
+ */
+ public static Feed shareUserUpdate(
+ String userId,
+ String postText,
+ String vreId,
+ String previewTitle,
+ String previewDescription,
+ String previewHost,
+ String previewUrl,
+ String urlThumbnail,
+ boolean notifyGroup
+ ) {
+
+ String escapedFeedText = org.gcube.social_networking.socialutillibrary.Utils.escapeHtmlAndTransformUrl(postText);
+
+ List hashtags = org.gcube.social_networking.socialutillibrary.Utils.getHashTags(postText);
+ if (hashtags != null && !hashtags.isEmpty())
+ escapedFeedText = org.gcube.social_networking.socialutillibrary.Utils.convertHashtagsAnchorHTML(escapedFeedText, hashtags);
+
+ GCubeUser user;
+
+ // retrieve group information
+ UserManager uManager = UserManagerWSBuilder.getInstance().getUserManager();
+ try{
+
+ user = uManager.getUserByUsername(userId);
+
+ }catch(Exception e){
+
+ logger.error("Unable to get user informations, feed write fails.", e);
+ return null;
+
+ }
+
+ String email = user.getEmail();
+ String fullName = user.getFirstName() + " " + user.getLastName();
+ String thumbnailURL = user.getUserAvatarURL();
+
+ String linkTitle = previewTitle == null ? "" : previewTitle;
+ String linkDesc = previewDescription == null ? "" : previewDescription;
+ String host = previewHost == null ? "" : previewHost;
+ String url = previewUrl == null ? "" : previewUrl;
+ if (urlThumbnail == null)
+ urlThumbnail = "null";
+
+ //this means the user has shared a file without text in it.
+ String textToPost = "";
+ if (escapedFeedText.trim().compareTo(NO_TEXT_FILE_SHARE) == 0) {
+ textToPost = org.gcube.social_networking.socialutillibrary.Utils.convertFileNameAnchorHTML(url);
+ } else {
+ textToPost = escapedFeedText;
+ }
+
+ Feed toShare = new Feed(UUID.randomUUID().toString(), FeedType.PUBLISH, userId, new Date(),
+ vreId, url, urlThumbnail, textToPost, PrivacyLevel.SINGLE_VRE, fullName, email, thumbnailURL, linkTitle, linkDesc, host);
+
+ logger.info("Attempting to save Feed with text: " + textToPost + " Level = " + PrivacyLevel.SINGLE_VRE + " Timeline = " + vreId);
+
+ boolean result = CassandraConnection.getInstance().getDatabookStore().saveUserFeed(toShare);
+
+ if(vreId != null && vreId.compareTo("") != 0 && result) {
+
+ logger.trace("Attempting to write onto " + vreId);
+
+ try {
+
+ try{
+ logger.info("Sleeping waiting for cassandra's update");
+ Thread.sleep(1000);
+
+ }catch(Exception e){
+
+ logger.error(e.toString());
+
+ }
+ CassandraConnection.getInstance().getDatabookStore().saveFeedToVRETimeline(toShare.getKey(), vreId);
+
+ if (hashtags != null && !hashtags.isEmpty())
+ CassandraConnection.getInstance().getDatabookStore().saveHashTags(toShare.getKey(), vreId, hashtags);
+
+ } catch (FeedIDNotFoundException e) {
+
+ logger.error("Error writing onto VRES Time Line" + vreId);
+ }
+
+ logger.trace("Success writing onto " + vreId);
+ }
+
+ if (!result)
+ return null;
+
+ //send the notification about this posts to everyone in the group if notifyGroup is true
+ if (vreId != null && vreId.compareTo("") != 0 && notifyGroup) {
+
+ try{
+
+ // get the site from the http request
+ SocialNetworkingSite site = SocialNetworkingSiteFinder.getSocialNetworkingSiteFromScope(ScopeProvider.instance.get());
+
+ // retrieve group information
+ GroupManager gManager = GroupManagerWSBuilder.getInstance().getGroupManager();
+
+ GCubeUser userInfo = uManager.getUserByUsername(userId);
+ SocialNetworkingUser socialUser =
+ new SocialNetworkingUser(userId, userInfo.getEmail(), userInfo.getFullname(), userInfo.getUserAvatarURL());
+
+
+ // handle the scope
+ String name = new ScopeBean(vreId).name(); // scope such as devVR
+ long groupId = gManager.getGroupId(name);
+ String groupName = gManager.getGroup(groupId).getGroupName();
+
+ logger.debug("Company id and name " + groupId + " " + groupName);
+
+ NotificationsManager nm = new ApplicationNotificationsManager(UserManagerWSBuilder.getInstance().getUserManager(), site, vreId, socialUser, NEWS_FEED_PORTLET_CLASSNAME);
+ new Thread(
+ new PostNotificationsThread(
+ UserManagerWSBuilder.getInstance().getUserManager(),
+ toShare.getKey(),
+ toShare.getDescription(),
+ ""+groupId,
+ nm,
+ new HashSet(),
+ new HashSet(hashtags))
+ ).start();
+
+ logger.debug("Start sending notifications for feed written by " + userId);
+ }catch(Exception e){
+ logger.error("Unable to notify users", e);
+ }
+ }
+ return toShare;
+ }
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/utils/TokensUtils.java b/src/main/java/org/gcube/portal/social/networking/ws/utils/TokensUtils.java
new file mode 100644
index 0000000..55f8322
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/utils/TokensUtils.java
@@ -0,0 +1,76 @@
+package org.gcube.portal.social.networking.ws.utils;
+
+import org.gcube.common.authorization.library.ClientType;
+import org.gcube.common.authorization.library.utils.Caller;
+
+
+/**
+ * Tokens utils methods
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class TokensUtils {
+
+ // a user context token (not qualified) has as qualifier the word "TOKEN"
+ private static final String DEFAULT_QUALIFIER_USER_TOKEN = "TOKEN";
+
+ /**
+ * Check if it is a service token
+ * @return a boolean value
+ */
+ public static boolean isServiceToken(Caller caller){
+
+ return caller.getClient().getType().equals(ClientType.SERVICE);
+
+ }
+
+ /**
+ * Check if it is an application token
+ * @return a boolean value
+ */
+ public static boolean isApplicationToken(Caller caller){
+
+ return caller.getClient().getType().equals(ClientType.EXTERNALSERVICE);
+
+ }
+
+ /**
+ * Check if it is a container token
+ * @return a boolean value
+ */
+ public static boolean isContainerToken(Caller caller){
+
+ return caller.getClient().getType().equals(ClientType.CONTAINER);
+
+ }
+
+ /**
+ * Check if it is a user token
+ * @return a boolean value
+ */
+ public static boolean isUserToken(Caller caller){
+
+ return caller.getClient().getType().equals(ClientType.USER);
+
+ }
+
+ /**
+ * Check if it is a user token (not qualified)
+ * @return a boolean value
+ */
+ public static boolean isUserTokenDefault(Caller caller){
+
+ return caller.getClient().getType().equals(ClientType.USER) && caller.getTokenQualifier().equals(DEFAULT_QUALIFIER_USER_TOKEN);
+
+ }
+
+ /**
+ * Check if it is a user token (qualified)
+ * @return a boolean value
+ */
+ public static boolean isUserTokenQualified(Caller caller){
+
+ return caller.getClient().getType().equals(ClientType.USER) && !caller.getTokenQualifier().equals(DEFAULT_QUALIFIER_USER_TOKEN);
+
+ }
+
+}
diff --git a/src/main/java/org/gcube/portal/social/networking/ws/utils/UserProfileExtendedWithVerifiedEmail.java b/src/main/java/org/gcube/portal/social/networking/ws/utils/UserProfileExtendedWithVerifiedEmail.java
new file mode 100644
index 0000000..3c7588f
--- /dev/null
+++ b/src/main/java/org/gcube/portal/social/networking/ws/utils/UserProfileExtendedWithVerifiedEmail.java
@@ -0,0 +1,194 @@
+package org.gcube.portal.social.networking.ws.utils;
+
+import java.util.List;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class UserProfileExtendedWithVerifiedEmail {
+
+ @JsonProperty("id")
+ private String username;
+
+ @JsonProperty("roles")
+ private List roles;
+
+ @JsonProperty("picture")
+ private String avatar;
+
+ @JsonProperty("name")
+ private String fullname;
+
+ @JsonProperty("middle_name")
+ private String middleName;
+
+ @JsonProperty("male")
+ private boolean male;
+
+ @JsonProperty("location_industry")
+ private String locationIndustry;
+
+ @JsonProperty("given_name")
+ private String firstName;
+
+ @JsonProperty("email")
+ private String email;
+
+ @JsonProperty("job_title")
+ private String jobTitle;
+
+ @JsonProperty("family_name")
+ private String lastName;
+
+ @JsonProperty("verified_email")
+ private boolean verifiedEmail = true;
+
+ public UserProfileExtendedWithVerifiedEmail() {
+ super();
+ }
+
+ /**
+ * @param username
+ * @param roles
+ * @param avatar
+ * @param fullname
+ */
+ public UserProfileExtendedWithVerifiedEmail(String username, List roles, String avatar, String fullname) {
+ this.username = username;
+ this.roles = roles;
+ this.avatar = avatar;
+ this.fullname = fullname;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public List getRoles() {
+ return roles;
+ }
+
+ public void setRoles(List roles) {
+ this.roles = roles;
+ }
+
+ public String getAvatar() {
+ return avatar;
+ }
+
+ public void setAvatar(String avatar) {
+ this.avatar = avatar;
+ }
+
+ public String getFullname() {
+ return fullname;
+ }
+
+ public void setFullname(String fullname) {
+ this.fullname = fullname;
+ }
+
+ public String getMiddleName() {
+ return middleName;
+ }
+
+ public void setMiddleName(String middleName) {
+ this.middleName = middleName;
+ }
+
+ public boolean isMale() {
+ return male;
+ }
+
+ public void setMale(boolean male) {
+ this.male = male;
+ }
+
+ public String getLocationIndustry() {
+ return locationIndustry;
+ }
+
+ public void setLocationIndustry(String locationIndustry) {
+ this.locationIndustry = locationIndustry;
+ }
+
+ public String getFirstName() {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName) {
+ this.firstName = firstName;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public String getJobTitle() {
+ return jobTitle;
+ }
+
+ public void setJobTitle(String jobTitle) {
+ this.jobTitle = jobTitle;
+ }
+
+ public String getLastName() {
+ return lastName;
+ }
+
+ public void setLastName(String lastName) {
+ this.lastName = lastName;
+ }
+
+ public boolean isVerifiedEmail() {
+ return verifiedEmail;
+ }
+
+ public void setVerifiedEmail(boolean verifiedEmail) {
+ this.verifiedEmail = verifiedEmail;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("UserProfileExtendedWithVerifiedEmail [username=");
+ builder.append(username);
+ builder.append(", roles=");
+ builder.append(roles);
+ builder.append(", avatar=");
+ builder.append(avatar);
+ builder.append(", fullname=");
+ builder.append(fullname);
+ builder.append(", middleName=");
+ builder.append(middleName);
+ builder.append(", male=");
+ builder.append(male);
+ builder.append(", locationIndustry=");
+ builder.append(locationIndustry);
+ builder.append(", firstName=");
+ builder.append(firstName);
+ builder.append(", email=");
+ builder.append(email);
+ builder.append(", jobTitle=");
+ builder.append(jobTitle);
+ builder.append(", lastName=");
+ builder.append(lastName);
+ builder.append(", verifiedEmail=");
+ builder.append(verifiedEmail);
+ builder.append("]");
+ return builder.toString();
+ }
+
+
+
+
+}
diff --git a/src/main/resources/config.properties b/src/main/resources/config.properties
new file mode 100644
index 0000000..01c7a5b
--- /dev/null
+++ b/src/main/resources/config.properties
@@ -0,0 +1,4 @@
+#These properties are used to build up the SocialNetworkingSite object to send notifications
+PROD_FALLBACK_GATEWAY=D4Science.org Gateway
+DEV_FALLBACK_GATEWAY=gCube Dev4 Snapshot Gateway
+PREPROD_FALLBACK_GATEWAY=gCube Preprod (dev) Gateway
\ No newline at end of file
diff --git a/src/main/resources/ehcache.xml b/src/main/resources/ehcache.xml
new file mode 100644
index 0000000..e00e3ad
--- /dev/null
+++ b/src/main/resources/ehcache.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/gcube-app.xml b/src/main/webapp/WEB-INF/gcube-app.xml
new file mode 100644
index 0000000..27ca951
--- /dev/null
+++ b/src/main/webapp/WEB-INF/gcube-app.xml
@@ -0,0 +1,11 @@
+
+ SocialNetworking
+ Portal
+ 2.1.0-SNAPSHOT
+ SocialNetworking Service
+
+ /rest/
+ /rest/swagger.yaml
+ /rest/swagger.json
+ /rest/application.wadl
+
\ No newline at end of file
diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..9a8bd29
--- /dev/null
+++ b/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,49 @@
+
+
+
+ Restful Web Application
+
+ jersey-servlet
+ org.glassfish.jersey.servlet.ServletContainer
+
+ jersey.config.beanValidation.enableOutputValidationErrorEntity.server
+ true
+
+
+ jersey.config.server.provider.packages
+
+ io.swagger.jaxrs.listing,
+ org.gcube.portal.social.networking
+
+
+ 1
+
+
+
+ SwaggerBootstrap
+ org.gcube.portal.social.networking.swagger.config.Bootstrap
+ 2
+
+
+
+ jersey-servlet
+ /rest/*
+
+
+
+ The token of the user J.A.R.V.I.S. on the portal (root context)
+ NOTIFIER_TOKEN
+
+
+
+
+ org.gcube.portal.social.networking.liferay.ws.ServletContextClass
+
+
+
+ index.jsp
+
+
diff --git a/src/main/webapp/index.jsp b/src/main/webapp/index.jsp
new file mode 100644
index 0000000..12ec4fa
--- /dev/null
+++ b/src/main/webapp/index.jsp
@@ -0,0 +1,5 @@
+
+
+ The social networking web service is up and running!
+
+
diff --git a/src/test/java/org/gcube/portal/test/GcoreEndpointReader.java b/src/test/java/org/gcube/portal/test/GcoreEndpointReader.java
new file mode 100644
index 0000000..6185e66
--- /dev/null
+++ b/src/test/java/org/gcube/portal/test/GcoreEndpointReader.java
@@ -0,0 +1,78 @@
+/**
+ *
+ */
+package org.gcube.portal.test;
+
+import static org.gcube.resources.discovery.icclient.ICFactory.client;
+import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
+
+import java.util.List;
+
+import org.gcube.common.resources.gcore.GCoreEndpoint;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.resources.discovery.client.api.DiscoveryClient;
+import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class GcoreEndpointReader {
+
+ private static final String resource = "jersey-servlet";
+ private static final String serviceName = "SocialNetworking";
+ private static final String serviceClass = "Portal";
+
+ private static Logger logger = LoggerFactory.getLogger(GcoreEndpointReader.class);
+ private String resourceEntyName;
+
+ /**
+ * Instantiates a new gcore endpoint reader.
+ *
+ * @param scope the scope
+ * @throws Exception the exception
+ */
+ public GcoreEndpointReader(String scope) throws Exception {
+
+ String currentScope = ScopeProvider.instance.get();
+
+ try{
+
+ logger.info("set scope "+scope);
+ ScopeProvider.instance.set(scope);
+
+ SimpleQuery query = queryFor(GCoreEndpoint.class);
+ query.addCondition(String.format("$resource/Profile/ServiceClass/text() eq '%s'",serviceClass));
+ query.addCondition("$resource/Profile/DeploymentData/Status/text() eq 'ready'");
+ query.addCondition(String.format("$resource/Profile/ServiceName/text() eq '%s'",serviceName));
+ query.setResult("$resource/Profile/AccessPoint/RunningInstanceInterfaces//Endpoint[@EntryName/string() eq \""+resource+"\"]/text()");
+
+ logger.debug("submitting quey "+query.toString());
+
+ DiscoveryClient client = client();
+ List endpoints = client.submit(query);
+ if (endpoints == null || endpoints.isEmpty()) throw new Exception("Cannot retrieve the GCoreEndpoint serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+scope);
+
+
+ this.resourceEntyName = endpoints.get(0);
+ if(resourceEntyName==null)
+ throw new Exception("Endpoint:"+resource+", is null for serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+scope);
+
+ logger.info("found entyname "+resourceEntyName+" for ckanResource: "+resource);
+
+ }catch(Exception e){
+ String error = "An error occurred during GCoreEndpoint discovery, serviceName: "+serviceName +", serviceClass: " +serviceClass +", in scope: "+scope +".";
+ logger.error(error, e);
+ throw new Exception(error);
+ }finally{
+ if(currentScope != null)
+ ScopeProvider.instance.set(currentScope);
+ }
+ }
+
+ /**
+ * @return the ResourceEntyName
+ */
+ public String getResourceEntyName() {
+
+ return resourceEntyName;
+ }
+}
diff --git a/src/test/java/org/gcube/portal/test/JTests.java b/src/test/java/org/gcube/portal/test/JTests.java
new file mode 100644
index 0000000..3d3a2c1
--- /dev/null
+++ b/src/test/java/org/gcube/portal/test/JTests.java
@@ -0,0 +1,201 @@
+package org.gcube.portal.test;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.AuthCache;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.protocol.HttpClientContext;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.auth.BasicScheme;
+import org.apache.http.impl.client.BasicAuthCache;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.portal.social.networking.liferay.ws.LiferayJSONWsCredentials;
+import org.gcube.portal.social.networking.ws.inputs.MessageInputBean;
+import org.gcube.portal.social.networking.ws.inputs.Recipient;
+import org.json.simple.JSONObject;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class JTests {
+
+ private static final String YOUR_TOKEN_HERE = "";
+ private static final String METHOD = "messages/writeMessageToUsers";
+ private static final String SCOPE = "/gcube";
+
+ //@Test
+ public void readSocialServiceEndPoint() throws Exception {
+
+ String findInContext = SCOPE;
+ ScopeProvider.instance.set(findInContext);
+
+ ServiceEndPointReaderSocial readerSE = new ServiceEndPointReaderSocial(findInContext);
+ System.out.println("Found base path " + readerSE.getBasePath());
+
+ }
+
+
+ //@Test
+ public void testWithApacheClient() throws Exception {
+
+ ServiceEndPointReaderSocial reader = new ServiceEndPointReaderSocial(SCOPE);
+ String requestForMessage = reader.getBasePath() + METHOD + "?gcube-token=" + YOUR_TOKEN_HERE;
+ requestForMessage = requestForMessage.replace("http", "https"); // remove the port (or set it to 443) otherwise you get an SSL error
+
+ System.out.println("Request url is going to be " + requestForMessage);
+
+ try(CloseableHttpClient client = HttpClientBuilder.create().build();){
+
+ HttpPost postRequest = new HttpPost(requestForMessage);
+
+ // put the sender, the recipients, subject and body of the mail here
+ StringEntity input = new StringEntity("sender=andrea.rossi&recipients=gianpaolo.coro&subject=Sample mail&body=Sample mail object");
+ input.setContentType("application/x-www-form-urlencoded");
+ postRequest.setEntity(input);
+
+ HttpResponse response = client.execute(postRequest);
+
+ System.out.println("Error is " + response.getStatusLine().getReasonPhrase());
+
+ if (response.getStatusLine().getStatusCode() != 201) {
+ throw new RuntimeException("Failed : HTTP error code : "
+ + response.getStatusLine().getStatusCode());
+ }
+
+ BufferedReader br = new BufferedReader(
+ new InputStreamReader((response.getEntity().getContent())));
+
+ String output;
+ System.out.println("Output from Server .... \n");
+ while ((output = br.readLine()) != null) {
+ System.out.println(output);
+ }
+
+ System.out.println(response.toString());
+
+ }catch(Exception e){
+ System.err.println("error while performing post method " + e.toString());
+ }
+ }
+
+ //@Test
+ public void parserJSON() throws IOException{
+
+ MessageInputBean message = new MessageInputBean();
+ message.setBody("a caso");
+ message.setSubject("subject");
+ ArrayList recipients = new ArrayList();
+ Recipient recipient = new Recipient("recipient1");
+ recipients.add(recipient);
+ message.setRecipients(recipients);
+
+
+ //Object mapper instance
+ ObjectMapper mapper = new ObjectMapper();
+
+ //Convert POJO to JSON
+ String json = mapper.writeValueAsString(message);
+
+ MessageInputBean obje = mapper.readValue(json, MessageInputBean.class);
+ System.out.println(json);
+ System.out.println(obje);
+
+ }
+
+ //@Test
+ public void callLiferayWS() throws Exception{
+
+ HttpHost target = new HttpHost("localhost", 8080, "http");
+
+ CredentialsProvider credsProvider = new BasicCredentialsProvider();
+ credsProvider.setCredentials(
+ new AuthScope(target.getHostName(), target.getPort()),
+ new UsernamePasswordCredentials("test@liferay.com", "random321"));
+ CloseableHttpClient httpclient = HttpClients.custom()
+ .setDefaultCredentialsProvider(credsProvider).build();
+ try {
+
+ // Create AuthCache instance
+ AuthCache authCache = new BasicAuthCache();
+ // Generate BASIC scheme object and add it to the local
+ // auth cache
+ BasicScheme basicAuth = new BasicScheme();
+ authCache.put(target, basicAuth);
+
+ // Add AuthCache to the execution context
+ HttpClientContext localContext = HttpClientContext.create();
+ localContext.setAuthCache(authCache);
+
+ HttpGet httpget = new HttpGet("/api/jsonws" + "/user/get-user-by-screen-name/company-id/20155/screen-name/costantino.perciante");
+
+ System.out.println("Executing request " + httpget.getRequestLine() + " to target " + target);
+ CloseableHttpResponse response = httpclient.execute(target, httpget, localContext);
+ try {
+ System.out.println("----------------------------------------");
+ System.out.println(response.getStatusLine());
+ System.out.println(EntityUtils.toString(response.getEntity()));
+ } finally {
+ response.close();
+ }
+ } finally {
+ httpclient.close();
+ }
+
+ }
+
+ //@Test
+ public void retrieveCredentials(){
+
+ ScopeProvider.instance.set("/gcube");
+ LiferayJSONWsCredentials cred = LiferayJSONWsCredentials.getSingleton();
+ System.out.println("Password is " + cred.getPassword());
+ System.out.println("Host is " + cred.getHost());
+
+ }
+
+ //@Test
+ public void readGcoreEndPoint() throws Exception{
+
+ GcoreEndpointReader reader = new GcoreEndpointReader("/gcube");
+ reader.getResourceEntyName();
+
+ }
+
+ //@Test
+ public void sendNotification() throws ClientProtocolException, IOException{
+
+ String url ="https://socialnetworking-d-d4s.d4science.org/social-networking-library-ws/rest//2/notifications/notify-job-status?gcube-token=07f5f961-d0e0-4bc4-af90-a305e8b63ac7-98187548";
+ CloseableHttpClient client = HttpClientBuilder.create().build();
+ JSONObject obj = new JSONObject();
+ obj.put("job_id", "bbbbb");
+ obj.put("recipient", "costantino.perciante");
+ obj.put("job_name", "aaaaaa");
+ obj.put("service_name", "Test");
+ obj.put("status", "SUCCEEDED");
+
+ HttpPost request = new HttpPost(url);
+ request.addHeader("Content-type", ContentType.APPLICATION_JSON.toString());
+ StringEntity paramsEntity = new StringEntity(obj.toJSONString(), ContentType.APPLICATION_JSON);
+ request.setEntity(paramsEntity);
+ client.execute(request);
+
+ }
+
+}
diff --git a/src/test/java/org/gcube/portal/test/ServiceEndPointReaderSocial.java b/src/test/java/org/gcube/portal/test/ServiceEndPointReaderSocial.java
new file mode 100644
index 0000000..a98a765
--- /dev/null
+++ b/src/test/java/org/gcube/portal/test/ServiceEndPointReaderSocial.java
@@ -0,0 +1,103 @@
+package org.gcube.portal.test;
+
+import static org.gcube.resources.discovery.icclient.ICFactory.clientFor;
+import static org.gcube.resources.discovery.icclient.ICFactory.queryFor;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.gcube.common.resources.gcore.ServiceEndpoint;
+import org.gcube.common.resources.gcore.ServiceEndpoint.AccessPoint;
+import org.gcube.common.scope.api.ScopeProvider;
+import org.gcube.resources.discovery.client.api.DiscoveryClient;
+import org.gcube.resources.discovery.client.queries.api.SimpleQuery;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Retrieves the base url of the social-networking service in the scope provided
+ * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+ */
+public class ServiceEndPointReaderSocial {
+
+ private String basePath = null;
+
+ private static Logger logger = LoggerFactory.getLogger(ServiceEndPointReaderSocial.class);
+ private final static String RUNTIME_RESOURCE_NAME = "SocialNetworking";
+ private final static String CATEGORY = "Portal";
+
+ public ServiceEndPointReaderSocial(String context){
+
+ if(context == null || context.isEmpty())
+ throw new IllegalArgumentException("A valid context is needed to discover the service");
+
+
+ String oldContext = ScopeProvider.instance.get();
+ ScopeProvider.instance.set(context);
+
+ try{
+
+ List resources = getConfigurationFromIS();
+ if (resources.size() == 0){
+ logger.error("There is no Runtime Resource having name " + RUNTIME_RESOURCE_NAME +" and Category " + CATEGORY + " in this scope.");
+ throw new Exception("There is no Runtime Resource having name " + RUNTIME_RESOURCE_NAME +" and Category " + CATEGORY + " in this scope.");
+ }
+ else {
+
+ for (ServiceEndpoint res : resources) {
+
+ Iterator accessPointIterator = res.profile().accessPoints().iterator();
+
+ while (accessPointIterator.hasNext()) {
+ ServiceEndpoint.AccessPoint accessPoint = (ServiceEndpoint.AccessPoint) accessPointIterator
+ .next();
+
+ // get base path
+ basePath = accessPoint.address();
+
+ // break
+ break;
+ }
+ }
+
+ }
+
+ }catch(Exception e){
+
+ logger.error("Unable to retrieve such service endpoint information!", e);
+
+ }finally{
+
+ if(oldContext != null && !oldContext.equals(context))
+ ScopeProvider.instance.set(oldContext);
+
+ }
+
+ logger.info("Found base path " + basePath + " for the service");
+
+ }
+
+ /**
+ * Retrieve endpoints information from IS for DB
+ * @return list of endpoints for ckan database
+ * @throws Exception
+ */
+ private List getConfigurationFromIS() throws Exception{
+
+ SimpleQuery query = queryFor(ServiceEndpoint.class);
+ query.addCondition("$resource/Profile/Name/text() eq '"+ RUNTIME_RESOURCE_NAME +"'");
+ query.addCondition("$resource/Profile/Category/text() eq '"+ CATEGORY +"'");
+ DiscoveryClient client = clientFor(ServiceEndpoint.class);
+ List toReturn = client.submit(query);
+ return toReturn;
+
+ }
+
+ /**
+ * Get the base path of the social networking service
+ * @return
+ */
+ public String getBasePath() {
+ return basePath;
+ }
+}