diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component index ea3d26e..65eedd1 100644 --- a/.settings/org.eclipse.wst.common.component +++ b/.settings/org.eclipse.wst.common.component @@ -4,7 +4,6 @@ - diff --git a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/client/StatisticsPanel.java b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/client/StatisticsPanel.java index 38309d5..e310412 100644 --- a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/client/StatisticsPanel.java +++ b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/client/StatisticsPanel.java @@ -15,6 +15,7 @@ import org.gcube.portlet.user.userstatisticsportlet.client.resources.Images; import org.gcube.portlet.user.userstatisticsportlet.client.ui.ActivityWidget; import org.gcube.portlet.user.userstatisticsportlet.client.ui.StatisticWidget; import org.gcube.portlet.user.userstatisticsportlet.shared.PostsStatsBean; +import org.gcube.portlet.user.userstatisticsportlet.shared.QuotaInfo; import org.gcube.portlet.user.userstatisticsportlet.shared.UserInformation; import com.github.gwtbootstrap.client.ui.AlertBlock; @@ -69,12 +70,14 @@ public class StatisticsPanel extends Composite { private final static String ACTIVITY_LABEL = "Activity"; private final static String LIKES_COMMENTS_GOT_LABEL = "Got"; private final static String STORAGE_LABEL = "Space Used"; + protected static final String QUOTA_LABEL = "Space Quota"; private final static String PROFILE_STRENGTH_LABEL = "Profile Strength"; private final static String SHOW_STATISTICS_OPTION_LABEL = "Show my statistics to VRE Members"; /** * Some tooltips */ + protected static final String TOOLTIP_QUOTA_SPACE = "Space used in the storage area is $USED of $ALLOWED"; private final static String TOOLTIP_ACTIVITY_ROOT_PROFILE = "Posts, likes, replies done during the last year"; private final static String TOOLTIP_ACTIVITY_VRE = "Posts, likes, replies done in the last year in this VRE"; private final static String TOOLTIP_GOT_ROOT_PROFILE = "Likes and post replies got during the last year"; @@ -310,8 +313,9 @@ public class StatisticsPanel extends Composite { // append widget mainPanel.add(activityGotWidgetContainer); - // the storage and the profile strength(only in root) + // the storage, quota, and the profile strength(only in root) final StatisticWidget storage = new StatisticWidget(isRoot); + final StatisticWidget quotaStorage = new StatisticWidget(isRoot); final StatisticWidget profileStrength = new StatisticWidget(isRoot); if(isRoot || isProfilePage){ @@ -326,6 +330,16 @@ public class StatisticsPanel extends Composite { mainPanel.add(storage); + quotaStorage.setHeader(QUOTA_LABEL); + quotaStorage.setToolTip(TOOLTIP_QUOTA_SPACE); + + // add loading image that will be replaced by the incoming values + Image quotaLoader = new Image(imagePath); + quotaLoader.setStyleName("loading-image-center-small"); + quotaStorage.appendToPanel(quotaLoader); + + mainPanel.add(quotaStorage); + profileStrength.setHeader(PROFILE_STRENGTH_LABEL); profileStrength.setToolTip(TOOLTIP_PROFILE_STRENGHT); @@ -363,6 +377,35 @@ public class StatisticsPanel extends Composite { } }); + // require quota information + statisticsService.getQuotaStorage(userid, new AsyncCallback() { + + @Override + public void onSuccess(QuotaInfo quota) { + + if(quota == null){ + quotaStorage.setVisible(false); + }else{ + long max = quota.getMax(); + long current = quota.getCurrent(); + long percent = Long.divideUnsigned(current, max); + quotaStorage.clearPanelValues(); + Button quotaStorageValue = new Button(); + quotaStorageValue.setType(ButtonType.LINK); + quotaStorageValue.setText(percent + "%"); + quotaStorageValue.addStyleName("buttons-statistics-disabled-events"); + quotaStorage.appendToPanel(quotaStorageValue); + } + + } + + @Override + public void onFailure(Throwable arg0) { + quotaStorage.setVisible(false); + } + }); + + // require profile strenght statisticsService.getProfileStrength(userid, new AsyncCallback() { @Override diff --git a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/client/UserStatisticsService.java b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/client/UserStatisticsService.java index d139f1d..a01300b 100644 --- a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/client/UserStatisticsService.java +++ b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/client/UserStatisticsService.java @@ -1,6 +1,7 @@ package org.gcube.portlet.user.userstatisticsportlet.client; import org.gcube.portlet.user.userstatisticsportlet.shared.PostsStatsBean; +import org.gcube.portlet.user.userstatisticsportlet.shared.QuotaInfo; import org.gcube.portlet.user.userstatisticsportlet.shared.UserInformation; import com.google.gwt.user.client.rpc.RemoteService; @@ -30,6 +31,11 @@ public interface UserStatisticsService extends RemoteService { */ String getTotalSpaceInUse(String userid); + /** + * get quota (about storage) information + */ + QuotaInfo getQuotaStorage(String userid); + /** * get profile strenght */ diff --git a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/client/UserStatisticsServiceAsync.java b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/client/UserStatisticsServiceAsync.java index c670c19..dcb410f 100644 --- a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/client/UserStatisticsServiceAsync.java +++ b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/client/UserStatisticsServiceAsync.java @@ -1,6 +1,7 @@ package org.gcube.portlet.user.userstatisticsportlet.client; import org.gcube.portlet.user.userstatisticsportlet.shared.PostsStatsBean; +import org.gcube.portlet.user.userstatisticsportlet.shared.QuotaInfo; import org.gcube.portlet.user.userstatisticsportlet.shared.UserInformation; import com.google.gwt.user.client.rpc.AsyncCallback; @@ -21,4 +22,6 @@ public interface UserStatisticsServiceAsync { void setShowMyOwnStatisticsToOtherPeople(boolean show, AsyncCallback callback); + + void getQuotaStorage(String userid, AsyncCallback callback); } diff --git a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/UserStatisticsServiceImpl.java b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/UserStatisticsServiceImpl.java index 2154279..efa87e5 100644 --- a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/UserStatisticsServiceImpl.java +++ b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/UserStatisticsServiceImpl.java @@ -1,5 +1,7 @@ package org.gcube.portlet.user.userstatisticsportlet.server; +import java.io.InputStream; +import java.net.URL; import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; @@ -7,17 +9,24 @@ import java.util.List; import java.util.Map; import java.util.Set; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathFactory; + import org.gcube.common.homelibrary.home.HomeLibrary; import org.gcube.common.homelibrary.home.workspace.Workspace; import org.gcube.common.portal.PortalContext; -import org.gcube.portal.custom.communitymanager.SiteManagerUtil; import org.gcube.portal.databook.server.DBCassandraAstyanaxImpl; import org.gcube.portal.databook.server.DatabookStore; import org.gcube.portal.databook.shared.Comment; import org.gcube.portal.databook.shared.Feed; import org.gcube.portlet.user.userstatisticsportlet.client.UserStatisticsService; +import org.gcube.portlet.user.userstatisticsportlet.server.cache.UserInfrastructureQuotaStorageCache; import org.gcube.portlet.user.userstatisticsportlet.server.cache.UserInfrastructureSpaceCache; +import org.gcube.portlet.user.userstatisticsportlet.server.utils.DiscoverQuotaServiceEndPoint; import org.gcube.portlet.user.userstatisticsportlet.shared.PostsStatsBean; +import org.gcube.portlet.user.userstatisticsportlet.shared.QuotaInfo; import org.gcube.portlet.user.userstatisticsportlet.shared.UserInformation; import org.gcube.vomanagement.usermanagement.GroupManager; import org.gcube.vomanagement.usermanagement.UserManager; @@ -25,6 +34,7 @@ import org.gcube.vomanagement.usermanagement.impl.LiferayGroupManager; import org.gcube.vomanagement.usermanagement.impl.LiferayUserManager; import org.gcube.vomanagement.usermanagement.model.GCubeGroup; import org.gcube.vomanagement.usermanagement.util.ManagementUtils; +import org.w3c.dom.Document; import com.google.gwt.user.server.rpc.RemoteServiceServlet; import com.liferay.portal.kernel.cache.CacheRegistryUtil; @@ -42,6 +52,7 @@ public class UserStatisticsServiceImpl extends RemoteServiceServlet implements U // private static final org.slf4j.Logger logger = LoggerFactory.getLogger(UserStatisticsServiceImpl.class); private static final Log logger = LogFactoryUtil.getLog(UserStatisticsServiceImpl.class); private DatabookStore store; + private String quotaServiceBaseUrl; private static final String CUSTOM_FIELD_NAME_USER_STATISTICS_VISIBILITY = "show_user_statistics_other_people"; private UserManager userManager = new LiferayUserManager(); private GroupManager groupManager = new LiferayGroupManager(); @@ -51,6 +62,9 @@ public class UserStatisticsServiceImpl extends RemoteServiceServlet implements U logger.info("Getting connection to Cassandra.."); store = new DBCassandraAstyanaxImpl(); ServerUtils.createUserCustomField(CUSTOM_FIELD_NAME_USER_STATISTICS_VISIBILITY, true); + + logger.info("Retrieving quota service endpoint"); + quotaServiceBaseUrl = DiscoverQuotaServiceEndPoint.discover(); } @Override @@ -59,6 +73,62 @@ public class UserStatisticsServiceImpl extends RemoteServiceServlet implements U store.closeConnection(); } + @Override + public QuotaInfo getQuotaStorage(String userid) { + + QuotaInfo toReturn = null; + String userName = null; + + if(quotaServiceBaseUrl != null){ + + userName = ServerUtils.getCurrentUser(this.getThreadLocalRequest()).getUsername(); + String quotaOfUser = userName; + + // do not show quota info to other users + if(userid != null && !userid.equals(userName)) + return null; + + logger.debug("Fetching info for quota of user " + quotaOfUser); + + try{ + + UserInfrastructureQuotaStorageCache cache = UserInfrastructureQuotaStorageCache.getCacheInstance(); + + if(cache.get(userName) != null) + toReturn = cache.get(userName); + else{ + + // ask the service ... + PortalContext pContext = PortalContext.getConfiguration(); + String rootContextToken = pContext.getCurrentUserToken("/" + pContext.getInfrastructureName(), userName); + URL request = new URL(quotaServiceBaseUrl + "?timeinterval=FOREVER&gcube-token=" + rootContextToken); + InputStream result = request.openStream(); + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document document = dBuilder.parse(result); + document.getDocumentElement().normalize(); + + logger.debug("Result is " + document); + // look for the properties + + // TO BE PARSED TODO + XPathFactory xpf = XPathFactory.newInstance(); + XPath xp = xpf.newXPath(); + //String text = xp.evaluate("//add[@job='351']/tag[position()=1]/text()", + // document.getDocumentElement()); + + // PUSH INTO THE CACHE + } + }catch(Exception e){ + logger.error("Failed to retrieve quota information for user", e); + } + + } + + logger.debug("Quota for user " + userName + " is " + toReturn); + return toReturn; + } + @Override public String getTotalSpaceInUse(String userid) { String storageInUse = null; diff --git a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/cache/UserInfrastructureQuotaStorageCache.java b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/cache/UserInfrastructureQuotaStorageCache.java new file mode 100644 index 0000000..940cc85 --- /dev/null +++ b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/cache/UserInfrastructureQuotaStorageCache.java @@ -0,0 +1,73 @@ +package org.gcube.portlet.user.userstatisticsportlet.server.cache; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.gcube.portlet.user.userstatisticsportlet.shared.QuotaInfo; + +import com.liferay.portal.kernel.log.Log; +import com.liferay.portal.kernel.log.LogFactoryUtil; + + +/** + * Cache for the user's storage quota max/in-use within the infrastructure + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class UserInfrastructureQuotaStorageCache implements CacheInterface { + + //private static final org.slf4j.Logger logger = LoggerFactory.getLogger(UserInfrastructureSpaceCache.class); + private static final Log logger = LogFactoryUtil.getLog(UserInfrastructureSpaceCache.class); + + /** + * The instance + */ + private static UserInfrastructureQuotaStorageCache instance = new UserInfrastructureQuotaStorageCache(); + + /** + * The hashmap + */ + private Map> userQuotaMap; + + /** + * Cache entry expires after EXPIRED_AFTER ms + */ + private static final long EXPIRED_AFTER = 1000 * 60 * 60; // an hour + + /** + * Private constructor + */ + private UserInfrastructureQuotaStorageCache(){ + userQuotaMap = new ConcurrentHashMap<>(); + } + + /** + * Retrieve the current cache instance object + * @return + */ + public static UserInfrastructureQuotaStorageCache getCacheInstance(){ + return instance; + } + + @Override + public QuotaInfo get(String key) { + + if(userQuotaMap.containsKey(key)){ + CacheValueBean bean = userQuotaMap.get(key); + if(CacheUtilities.expired(bean.getTTL(), EXPIRED_AFTER)){ + userQuotaMap.remove(key); + logger.debug("Amount of space in the infrastructure used expired for key " + key + ", returning null"); + } + else + return bean.getValue(); + } + return null; + } + + @Override + public boolean insert(String key, QuotaInfo value) { + CacheValueBean newBean = new CacheValueBean(value, System.currentTimeMillis()); + userQuotaMap.put(key, newBean); + return true; + } + +} diff --git a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/cache/UserInfrastructureSpaceCache.java b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/cache/UserInfrastructureSpaceCache.java index 72871ec..dbb4131 100644 --- a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/cache/UserInfrastructureSpaceCache.java +++ b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/cache/UserInfrastructureSpaceCache.java @@ -8,8 +8,7 @@ import com.liferay.portal.kernel.log.LogFactoryUtil; /** * Cache for the user's space in use within the infrastructure - * @author Costantino Perciante at ISTI-CNR - * (costantino.perciante@isti.cnr.it) + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) */ public class UserInfrastructureSpaceCache implements CacheInterface { diff --git a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/utils/DiscoverQuotaServiceEndPoint.java b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/utils/DiscoverQuotaServiceEndPoint.java new file mode 100644 index 0000000..b5e7c97 --- /dev/null +++ b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/server/utils/DiscoverQuotaServiceEndPoint.java @@ -0,0 +1,91 @@ +package org.gcube.portlet.user.userstatisticsportlet.server.utils; + +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.portal.PortalContext; +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 com.liferay.portal.kernel.log.Log; +import com.liferay.portal.kernel.log.LogFactoryUtil; + + +/** + * Discover the base path of the QuotaServiceEndPoint + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class DiscoverQuotaServiceEndPoint { + + private static final Log logger = LogFactoryUtil.getLog(DiscoverQuotaServiceEndPoint.class); + + // info to retrieve it + private static final String SERVICE_TYPE = "Quota"; + private static final String SERVICE_NAME = "Persistence"; + private static final String SERVICE_PROPERTY = "urlService"; + + public static String discover(){ + + // this service endpoint needs to be discovered into the root + String basePath = null; + String currentContext = ScopeProvider.instance.get(); + try{ + + PortalContext pc = PortalContext.getConfiguration(); + String rootScope = "/" + pc.getInfrastructureName(); + ScopeProvider.instance.set(rootScope); + + // perform the query + logger.info("Looking up " + SERVICE_TYPE + " service in context " + rootScope); + List resources = getConfigurationFromIS(); + + if(resources == null || resources.isEmpty()) + throw new RuntimeException("Missing service endpoint for " + SERVICE_TYPE); + else{ + + Iterator accessPointIterator = resources.get(0).profile().accessPoints().iterator(); + while (accessPointIterator.hasNext()) { + ServiceEndpoint.AccessPoint accessPoint = (ServiceEndpoint.AccessPoint) accessPointIterator + .next(); + + Map propertiesMap = accessPoint.propertyMap(); + basePath = (String)propertiesMap.get(SERVICE_PROPERTY).value(); + + } + + } + basePath = basePath == null ? basePath : basePath + "gcube/service/quotaStatus/detail"; + logger.info("Base path value is " + basePath); + }catch(Exception e){ + logger.error("The following errore arose while looking up service endpoint for " + SERVICE_TYPE, e); + }finally{ + ScopeProvider.instance.set(currentContext); + } + + return basePath; + } + + /** + * Retrieve endpoints information from IS + * @return the service endpoint + * @throws Exception + */ + private static List getConfigurationFromIS() throws Exception{ + + SimpleQuery query = queryFor(ServiceEndpoint.class); + query.addCondition("$resource/Profile/Name/text() eq '"+ SERVICE_NAME +"'"); + query.addCondition("$resource/Profile/Category/text() eq '"+ SERVICE_TYPE +"'"); + DiscoveryClient client = clientFor(ServiceEndpoint.class); + List toReturn = client.submit(query); + return toReturn; + + } +} diff --git a/src/main/java/org/gcube/portlet/user/userstatisticsportlet/shared/QuotaInfo.java b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/shared/QuotaInfo.java new file mode 100644 index 0000000..ecee81a --- /dev/null +++ b/src/main/java/org/gcube/portlet/user/userstatisticsportlet/shared/QuotaInfo.java @@ -0,0 +1,40 @@ +package org.gcube.portlet.user.userstatisticsportlet.shared; + +import java.io.Serializable; + +/** + * A quota info bean: maximum and current value are contained in it. + * @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it) + */ +public class QuotaInfo implements Serializable{ + + private static final long serialVersionUID = 1L; + private long max; + private long current; + + public QuotaInfo() { + super(); + } + + public QuotaInfo(long max, long current) { + super(); + this.max = max; + this.current = current; + } + public long getMax() { + return max; + } + public void setMax(long max) { + this.max = max; + } + public long getCurrent() { + return current; + } + public void setCurrent(long current) { + this.current = current; + } + @Override + public String toString() { + return "QuotaInfo [max=" + max + ", current=" + current + "]"; + } +}