From 8d851ca255a1f3f084d290c386bb213d9e699994 Mon Sep 17 00:00:00 2001 From: Massimiliano Assante Date: Tue, 21 Feb 2017 10:55:08 +0000 Subject: [PATCH] allocation map takes into account the infrastructure context git-svn-id: https://svn.research-infrastructures.eu/d4science/gcube/trunk/portlets/user/rstudio-wrapper-portlet@142831 82a268e6-3cf1-43bd-a215-b396298e98cf --- .../server/RStudioServiceImpl.java | 137 +++++++++++------- .../shared/FieldVerifier.java | 42 ------ .../shared/RStudioInstance.java | 29 ++++ 3 files changed, 110 insertions(+), 98 deletions(-) delete mode 100644 src/main/java/org/gcube/portlets/user/rstudio_wrapper_portlet/shared/FieldVerifier.java create mode 100644 src/main/java/org/gcube/portlets/user/rstudio_wrapper_portlet/shared/RStudioInstance.java diff --git a/src/main/java/org/gcube/portlets/user/rstudio_wrapper_portlet/server/RStudioServiceImpl.java b/src/main/java/org/gcube/portlets/user/rstudio_wrapper_portlet/server/RStudioServiceImpl.java index bc4bac9..66fa692 100644 --- a/src/main/java/org/gcube/portlets/user/rstudio_wrapper_portlet/server/RStudioServiceImpl.java +++ b/src/main/java/org/gcube/portlets/user/rstudio_wrapper_portlet/server/RStudioServiceImpl.java @@ -7,11 +7,13 @@ import static org.gcube.resources.discovery.icclient.ICFactory.queryFor; import java.net.MalformedURLException; import java.util.HashMap; 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.scope.api.ScopeProvider; import org.gcube.portlets.user.rstudio_wrapper_portlet.client.RStudioService; +import org.gcube.portlets.user.rstudio_wrapper_portlet.shared.RStudioInstance; import org.gcube.resources.discovery.client.api.DiscoveryClient; import org.gcube.resources.discovery.client.queries.api.SimpleQuery; import org.gcube.vomanagement.usermanagement.UserManager; @@ -32,10 +34,11 @@ public class RStudioServiceImpl extends RemoteServiceServlet implements RStudioS private static final Logger _log = LoggerFactory.getLogger(RStudioServiceImpl.class); + private static final String RSTUDIO_INSTANCES = "RStudio-Instances"; 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"; - + private static final String SERVICE_EP_NAME = "RConnector"; private static final String CATEGORY = "DataAnalysis"; public static final String USER_ID_ATTR_NAME = "gcube-userId"; @@ -48,6 +51,9 @@ public class RStudioServiceImpl extends RemoteServiceServlet implements RStudioS // user = "costantino.perciante"; return user; } + /** + * + */ @Override public String retrieveRStudioSecureURL() throws IllegalArgumentException { String toReturn = ""; @@ -78,20 +84,31 @@ public class RStudioServiceImpl extends RemoteServiceServlet implements RStudioS _log.debug("calling rConnector with scope = " + scope + " and token = "+token ); List resources = getRStudioServiceEndpoints(scope); if (resources.size() == 0){ - _log.warn("There is no Servcie Endpoint having CATEGORY " + CATEGORY +" and NAME " + SERVICE_EP_NAME + " in this scope. Returning default instance"); + _log.warn("There is no Service Endpoint having CATEGORY " + CATEGORY +" and NAME " + SERVICE_EP_NAME + " 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 ... "); - //check RStudio + String hostedOnSet = null; + + //check if an RStudio instance was previously set (RETRO-COMPATIBILY) if (um.readCustomAttr(userId, RSTUDIO_URL) != null && um.readCustomAttr(userId, RSTUDIO_URL).toString().compareTo("") != 0) { - String hostedOn = (String) um.readCustomAttr(userId, RSTUDIO_URL); - _log.info("**** 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); + _log.debug("User had already an RStudio Instance set, upgrading to new version "); + hostedOnSet = (String) um.readCustomAttr(userId, RSTUDIO_URL); + writeRStudioInstanceInScope(um, curUser, scope, hostedOnSet); + um.saveCustomAttr(userId, RSTUDIO_URL, ""); //reset the old value to blank so that from now on it will read from the new field + } + + hostedOnSet = getUserRStudioInstances(um,curUser).get(scope); + + _log.info("**** Checking if still exist on this scope: " + scope); + //if the instance exists and is still available in the given context + if (hostedOnSet != null && checkRStudioInstanceExistence(curUser, hostedOnSet, resources) ) { + toReturn = getRConnectorURL(hostedOnSet, token); + _log.info("User " + curUser.getFullname() + " has RStudio Instance set and is valid, returning rConnector URL " + toReturn); } else {//need to find the RStudio - _log.info("User " + curUser.getFullname() + "DOES NOT HAVE RStudio Instance set, calculating allocation ... "); + _log.info("User " + curUser.getFullname() + " DOES NOT have RStudio Instance set or the instance previous set no longer exists, calculating allocation ... "); HashMap rStudioDistributionMap = new HashMap<>(); for (ServiceEndpoint res : resources) { String hostedOn = res.profile().runtime().hostedOn(); @@ -100,8 +117,8 @@ public class RStudioServiceImpl extends RemoteServiceServlet implements RStudioS 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 (getUserRStudioInstances(um,gCubeUser).get(scope) != null) { + String hostedOn = getUserRStudioInstances(um,gCubeUser).get(scope); if (rStudioDistributionMap.containsKey(hostedOn)) { int noToSet = rStudioDistributionMap.get(hostedOn)+1; rStudioDistributionMap.put(hostedOn, noToSet); @@ -126,8 +143,8 @@ public class RStudioServiceImpl extends RemoteServiceServlet implements RStudioS } i++; } - um.saveCustomAttr(userId, RSTUDIO_URL, host2Select); - _log.debug("User " + curUser.getFullname() + " RStudio Instance set calculated = " + host2Select); + writeRStudioInstanceInScope(um, curUser, scope, host2Select); + _log.debug("User " + curUser.getFullname() + " RStudio Instance set calculated = " + host2Select + " for context " + scope); toReturn = getRConnectorURL(host2Select, token); _log.debug("User " + curUser.getFullname() + " has RStudio Instance set, returning rConnector URL " + toReturn); } @@ -140,6 +157,49 @@ public class RStudioServiceImpl extends RemoteServiceServlet implements RStudioS return toReturn; } + /** + * + * @param um + * @param gCubeUser + * @return a map containing the scope and the RStudio Instances set for this user + * @throws UserRetrievalFault + */ + private Map getUserRStudioInstances(UserManager um, GCubeUser gCubeUser) throws UserRetrievalFault { + Map theInstances = new HashMap<>(); + if (um.readCustomAttr(gCubeUser.getUserId(), RSTUDIO_INSTANCES) != null && um.readCustomAttr(gCubeUser.getUserId(), RSTUDIO_INSTANCES).toString().compareTo("") != 0) { + String[] values = (String[]) um.readCustomAttr(gCubeUser.getUserId(), RSTUDIO_INSTANCES); + if (values != null && values.length > 0) { + for (int i = 0; i < values.length; i++) { + String[] splits = values[i].split("\\|"); + RStudioInstance istance = new RStudioInstance(splits); + theInstances.put(istance.getContext(), istance.getHostedOn()); + } + } + } + return theInstances; + } + /** + * write the RStudio Instance + * @param um + * @param gCubeUser + * @param context the infra scope + * @param hostedOn + * @throws UserRetrievalFault + */ + private void writeRStudioInstanceInScope(UserManager um, GCubeUser gCubeUser, String context, String hostedOn) throws UserRetrievalFault { + Map theInstances = getUserRStudioInstances(um, gCubeUser); + theInstances.put(context, hostedOn); + String[] theValues = new String[theInstances.size()]; + int i = 0; + for (String instanceScope : theInstances.keySet()) { + String toPut = instanceScope+"|"+theInstances.get(instanceScope); //-> /gcube/devNext/NextNext|rstudio.d4science.org:443 + theValues[i] = toPut; + i++; + } + //overwrite the values + um.saveCustomAttr(gCubeUser.getUserId(), RSTUDIO_INSTANCES, theValues); + } + private String getRConnectorURL(String hostedOn, String token) throws MalformedURLException, IllegalArgumentException { String[] splits = hostedOn.split(":"); int port = 80; @@ -155,33 +215,6 @@ public class RStudioServiceImpl extends RemoteServiceServlet implements RStudioS return "http://"+hostedOn+PATH_TO_RCONNECTOR+"?gcube-token="+token; } } - -// private String getRConnectorURL(String hostedOn, String token) { -// _log.info("getRConnectorURL hostedOn:" + hostedOn); -// String[] splits = hostedOn.split(":"); -// String host = splits[0]; -// int port = 80; -// try { -// port = Integer.parseInt(splits[1]); -// } catch (Exception e) { -// _log.warn("Could not find an integer after :, using default port 80"); -// } -// -// String toReturn = ""; -// try { -// toReturn = rConnector().at(host, port).build().connect().toURL().toExternalForm(); -// } catch (MalformedURLException | IllegalArgumentException e) { -// e.printStackTrace(); -// return null; -// } -// if ((port == 443 || port == 8443) && !toReturn.startsWith("https")) { -// toReturn = toReturn.replaceFirst("http", "https"); -// return toReturn; -// } -// else { -// return toReturn; -// } -// } /** * * @return the @@ -199,21 +232,13 @@ public class RStudioServiceImpl extends RemoteServiceServlet implements RStudioS ScopeProvider.instance.set(currScope); return toReturn; } -// /** -// * -// * @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; -// } + + private static boolean checkRStudioInstanceExistence(GCubeUser curUser, String hostedOn, List resources) { + for (ServiceEndpoint serviceEndpoint : resources) + if (serviceEndpoint.profile().runtime().hostedOn().equals(hostedOn)) { + _log.info("**** The instance previously set for user " + curUser.getFullname() + " on " + hostedOn + " is still valid"); + return true; + } + return false; + } } diff --git a/src/main/java/org/gcube/portlets/user/rstudio_wrapper_portlet/shared/FieldVerifier.java b/src/main/java/org/gcube/portlets/user/rstudio_wrapper_portlet/shared/FieldVerifier.java deleted file mode 100644 index 52e941c..0000000 --- a/src/main/java/org/gcube/portlets/user/rstudio_wrapper_portlet/shared/FieldVerifier.java +++ /dev/null @@ -1,42 +0,0 @@ -package org.gcube.portlets.user.rstudio_wrapper_portlet.shared; - -/** - *

- * FieldVerifier validates that the name the user enters is valid. - *

- *

- * This class is in the shared packing because we use it in both - * the client code and on the server. On the client, we verify that the name is - * valid before sending an RPC request so the user doesn't have to wait for a - * network round trip to get feedback. On the server, we verify that the name is - * correct to ensure that the input is correct regardless of where the RPC - * originates. - *

- *

- * When creating a class that is used on both the client and the server, be sure - * that all code is translatable and does not use native JavaScript. Code that - * is note translatable (such as code that interacts with a database or the file - * system) cannot be compiled into client side JavaScript. Code that uses native - * JavaScript (such as Widgets) cannot be run on the server. - *

- */ -public class FieldVerifier { - - /** - * Verifies that the specified name is valid for our service. - * - * In this example, we only require that the name is at least four - * characters. In your application, you can use more complex checks to ensure - * that usernames, passwords, email addresses, URLs, and other fields have the - * proper syntax. - * - * @param name the name to validate - * @return true if valid, false if invalid - */ - public static boolean isValidName(String name) { - if (name == null) { - return false; - } - return name.length() > 3; - } -} diff --git a/src/main/java/org/gcube/portlets/user/rstudio_wrapper_portlet/shared/RStudioInstance.java b/src/main/java/org/gcube/portlets/user/rstudio_wrapper_portlet/shared/RStudioInstance.java new file mode 100644 index 0000000..f14a3be --- /dev/null +++ b/src/main/java/org/gcube/portlets/user/rstudio_wrapper_portlet/shared/RStudioInstance.java @@ -0,0 +1,29 @@ +package org.gcube.portlets.user.rstudio_wrapper_portlet.shared; + +public class RStudioInstance { + String context; + String hostedOn; + + public RStudioInstance(String[] splits) { + this.context = splits[0]; + this.hostedOn = splits[1]; + } + + public RStudioInstance(String context, String hostedOn) { + this.hostedOn = hostedOn; + this.context = context; + } + public String getHostedOn() { + return hostedOn; + } + public void setHostedOn(String hostedOn) { + this.hostedOn = hostedOn; + } + public String getContext() { + return context; + } + public void setContext(String context) { + this.context = context; + } + +}