package org.gcube.portlets.user.rstudio_wrapper_portlet.server; import static org.gcube.data.analysis.rconnector.client.Constants.rConnector; import static org.gcube.resources.discovery.icclient.ICFactory.clientFor; import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; import java.net.MalformedURLException; import java.util.HashMap; import java.util.List; import org.gcube.common.portal.PortalContext; import org.gcube.common.resources.gcore.GCoreEndpoint; import org.gcube.common.resources.gcore.GCoreEndpoint.Profile.Endpoint; import org.gcube.common.scope.api.ScopeProvider; import org.gcube.portlets.user.rstudio_wrapper_portlet.client.RStudioService; import org.gcube.resources.discovery.client.api.DiscoveryClient; import org.gcube.resources.discovery.client.queries.api.SimpleQuery; import org.gcube.vomanagement.usermanagement.UserManager; import org.gcube.vomanagement.usermanagement.impl.LiferayUserManager; import org.gcube.vomanagement.usermanagement.model.GCubeUser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.gwt.user.server.rpc.RemoteServiceServlet; /** * The server side implementation of the RPC service. */ @SuppressWarnings("serial") public class RStudioServiceImpl extends RemoteServiceServlet implements RStudioService { private static final Logger _log = LoggerFactory.getLogger(RStudioServiceImpl.class); private static final String SERVICE_NAME = "RConnector"; private static final String SERVICECLASS = "DataAnalysis"; private static final String RSTUDIO_URL = "RStudio-URL"; private static final String RCONNECTOR_EntryName = "org.gcube.data.analysis.rconnector.RConnector"; private static final String PATH_TO_RCONNECTOR = "/r-connector/gcube/service/connect"; /** * when packaging test will fail if the user is not set to test.user * @return . */ public String getDevelopmentUser() { String user = "test.user"; // user = "costantino.perciante"; return user; } @Override public String retrieveRStudioSecureURL() throws IllegalArgumentException { String toReturn = ""; PortalContext pContext = PortalContext.getConfiguration(); GCubeUser curUser = pContext.getCurrentUser(getThreadLocalRequest()); long userId = curUser.getUserId(); String scope = pContext.getCurrentScope(getThreadLocalRequest()); String token = pContext.getCurrentUserToken(scope, userId); try { _log.debug("calling rConnector with scope = " + scope + " and token = "+token ); List resources = getRStudioServiceEndpoints(scope); if (resources.size() == 0){ _log.warn("There is no gCORE Endpoint having name " + SERVICE_NAME +" and CLASS " + SERVICECLASS + " in this scope. Returning default instance"); toReturn = rConnector().build().connect().toURL().toExternalForm(); } else { UserManager um = new LiferayUserManager(); _log.debug("Checking if user " + curUser.getFullname() + " has already an RStudio Instance set ... "); //chec RStudio if (um.readCustomAttr(userId, RSTUDIO_URL) != null && um.readCustomAttr(userId, RSTUDIO_URL).toString().compareTo("") != 0) { String hostedOn = (String) um.readCustomAttr(userId, RSTUDIO_URL); _log.debug("**** The instance set for user " + curUser.getFullname() + " is " + hostedOn); toReturn = getRConnectorURL(hostedOn, token); _log.debug("User " + curUser.getFullname() + " has RStudio Instance set, returning rConnector URL " + toReturn); } else {//need to find the RStudio _log.debug("User " + curUser.getFullname() + "DOES NOT HAVE RStudio Instance set, calculating allocation ... "); HashMap rStudioDistributionMap = new HashMap<>(); for (GCoreEndpoint res : resources) { Endpoint ep = res.profile().endpointMap().get(RCONNECTOR_EntryName); String hostedOn = ep.uri().getHost()+":"+ep.uri().getPort(); rStudioDistributionMap.put(hostedOn, 0); } List vreUsers = um.listUsersByGroup(pContext.getCurrentGroupId(getThreadLocalRequest()), false); _log.debug("VRE " + scope + " has totalUsers = " + vreUsers.size()); for (GCubeUser gCubeUser : vreUsers) { if (um.readCustomAttr(gCubeUser.getUserId(), RSTUDIO_URL) != null && um.readCustomAttr(gCubeUser.getUserId(), RSTUDIO_URL).toString().compareTo("") != 0) { String hostedOn = (String) um.readCustomAttr(gCubeUser.getUserId(), RSTUDIO_URL); if (rStudioDistributionMap.containsKey(hostedOn)) { int noToSet = rStudioDistributionMap.get(hostedOn)+1; rStudioDistributionMap.put(hostedOn, noToSet); _log.debug("rStudioDistributionMap for = " + hostedOn + " -> " + noToSet); } } } _log.debug("VRE - RStudio allocaiton map as follows: "); int min = 0; int i = 0; String host2Select = ""; for (String host : rStudioDistributionMap.keySet()) { _log.debug("Host " + host + " has # users=" + rStudioDistributionMap.get(host)); if (i==0) { host2Select = host; min = rStudioDistributionMap.get(host); } else { int usersNo = rStudioDistributionMap.get(host); if (usersNo < min) { _log.debug("Host " + host + " has LESS users than " + host2Select + " updating"); host2Select = host; } } i++; } um.saveCustomAttr(userId, RSTUDIO_URL, host2Select); _log.debug("User " + curUser.getFullname() + " RStudio Instance set calculated = " + host2Select); toReturn = getRConnectorURL(host2Select, token); _log.debug("User " + curUser.getFullname() + " has RStudio Instance set, returning rConnector URL " + toReturn); } } } catch (Exception e) { e.printStackTrace(); } _log.info("returning URL from rConnector = "+toReturn); return toReturn; } private String getRConnectorURL(String hostedOn, String token) throws MalformedURLException, IllegalArgumentException { String[] splits = hostedOn.split(":"); int port = 80; try { port = Integer.parseInt(splits[1]); } catch (Exception e) { _log.warn("Could not find an integer after :, using default port 80"); } //http://rstudio-dev.d4science.org:80/r-connector/gcube/service/connect?gcube-token=6fa92d94-6568-4510-8443-a1c5ecdf1c7d-98187548s if (port == 443 || port == 8443) return "https://"+hostedOn+PATH_TO_RCONNECTOR+"?gcube-token="+token; else { return "http://"+hostedOn+PATH_TO_RCONNECTOR+"?gcube-token="+token; } } /** * * @return the * @throws Exception */ private List getRStudioServiceEndpoints(String scope) throws Exception { _log.debug("getRStudioServiceEndpoints on scope="+scope ); String currScope = ScopeProvider.instance.get(); ScopeProvider.instance.set(scope); SimpleQuery query = queryFor(GCoreEndpoint.class); query.addCondition("$resource/Profile/ServiceName/text() eq '"+ SERVICE_NAME +"'"); query.addCondition("$resource/Profile/ServiceClass/text() eq '"+ SERVICECLASS +"'"); DiscoveryClient client = clientFor(GCoreEndpoint.class); List toReturn = client.submit(query); ScopeProvider.instance.set(currScope); return toReturn; } }