Difference between revisions of "ClientContextLibrary"

From Gcube Wiki
Jump to: navigation, search
(Tips for Development Environments)
(Case 2 - HTTP GET or POST made with GWT RequestBuilder)
 
(23 intermediate revisions by 2 users not shown)
Line 2: Line 2:
 
=Client Context library for gCube Portlets =
 
=Client Context library for gCube Portlets =
  
Starting from '''gCube 4.2''', a new mechanism to retrieve the current user identifier and the current context identifier at client side has been introduced for gCube's portlets. The ''Client Context library'' allows to easily retrieve these information by relying on the ''Liferay.ThemeDisplay'' javascript object <ref>[https://www.liferay.com/it Liferay] is the portal technology adopted in the D4Science Infrastructure which is built upon the gCube software</ref>, and inject them at server side (they are transparently sent along the header of the remote requests). At server side, you should use the PortalManager library<ref>More information about the PortalManager are reported [https://wiki.gcube-system.org/gcube/Portal_Manager here]</ref>, to retrieve and convert them automatically to the current user's username and gCube context.
+
Starting from '''gCube 4.2''', the current Context identifier resides client side. If your Portlet does use '''AJAX calls''' (i.e. XMLHttpRequest to exchange data with a server behind the scenes) '''you don't need this component'''. Everything is already set up for you by the gCube Portal/Gateway in which your application is running.
  
= The Client Context Widget =
+
If you use standard http GET or POST to exchange data with the server instead, and you need to pass to the server the current Infrastructure scope identifier (the Liferay groupId).
  
The Client Context has been developed as a GWT Widget. Its source code is available in the gCube SVN repository, at this [http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/portal/client-context-library url]. Basically, it offers two self-explanatory methods
+
== Getting the Client Context in Javascript ==
 
+
<source lang="xml">
<source lang="java">
+
<script>
public static native String getCurrentContextId();
+
var groupId = Liferay.ThemeDisplay.getScopeGroupId();
 +
</script>
 
</source>
 
</source>
  
and
+
Read more about Liferay Javascript Object: [1]
  
<source lang="java">
+
== Getting the Client Context in GWT (Widget) ==
public static native String getCurrentUserId();
+
</source>
+
  
This code is translated to native Javascript code, starting from pure Java code by the GWT Framework. Please note that both methods actually return a number value.
+
The Client Context has been wrapped as a GWT Widget. Its source code is available in the gCube SVN repository, at this [http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/portal/client-context-library url].  
  
 
To use the widget, you need to declare the following maven dependency in your project's pom.xml
 
To use the widget, you need to declare the following maven dependency in your project's pom.xml
Line 38: Line 37:
 
</source>
 
</source>
  
Finally, in the ''onModuleLoad()'' method of your gwt web application, you must declare the following code line
+
then you just use the GCubeClientContext.getCurrentContextId() method:
  
 
<source lang="java">
 
<source lang="java">
public class ... implements EntryPoint {
+
public static native String getCurrentContextId();
+
</source>
  public void onModuleLoad() {
+
 
 
+
Either if you pass this identifier explicitly as parameter, or embed it in the header of your call, in the server you can use the following method to get the infrastructure scope (note: scopeGroupId parameter is the value returned from the GCubeClientContext.getCurrentContextId() method described previously:
    /**
+
 
      * Inject client context to automatically pass the context id and the user id to the server side
+
<source lang="java">
      */
+
import org.gcube.common.portal;
      GCubeClientContext.injectContext();
+
...
     
+
PortalContext pContext = PortalContext.getConfiguration();
      ...
+
 
  }  
+
/**
}
+
*  
 +
* @param scopeGroupId the liferay groupid number (as String) of the VRE/VO
 +
* @return the scope (context)
 +
*/
 +
String currentScope = pContext.getCurrentScope(String scopeGroupId);
  
 
</source>
 
</source>
  
== Portlet Example ==
+
==Examples for passing the Client Context via Standard HTTP POST or GET ==
  
A portlet example's source code is available [http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/portlets/user/client-context-example url]. It uses both the Client Context library and the PortalManager library, together with the UserManagement library <ref>More information about the UserManagement library are reported [https://wiki.gcube-system.org/gcube/UserManagement_Core here]</ref>, which abstracts Liferay's concepts and map them to gCube's ones. Once deployed on Liferay, the portlets looks like
+
It can happen that in your web applications you have to use standard HTTP POST or GET for your servlet. This is true, for instance, when you have to '''upload a file from the browser'''.  
  
[[File:Contextclientexample.png |200px]]
+
Other cases may involve the usage of the GWT '''RequestBuilder''' object (com.google.gwt.http.client.RequestBuilder) for standard HTTP Calls.
  
As you can see, the Javascript client code automatically set the user identifier and the context identifier by using the ClientContext methods reported above. By pushing on the button '''Retrieve''', a remote request that has in the header the group id and the user id is sent to the server side, and the Portal Manager is used to retrieve the current user (given the user identifier) and the context (given the group id), as follows:
+
For both cases we here suggests to possible solution, in order to pass the ClientContext to your servlets.
 +
 
 +
=== Case 1 - HTTP POST requests ===
 +
 
 +
A possible approach is to use one (HTML) Hidden field in the form and to read this hidden field value in the doPost servlet's method.
 +
 
 +
The following example is written in GWT.
  
 
<source lang="java">
 
<source lang="java">
/**
+
//client side code
* Server side implementation.
+
...
* @author Massimiliano Assante at ISTI-CNR (massimiliano.assante@isti.cnr.it)
+
VerticalPanel panel = new VerticalPanel();
* @author Costantino Perciante at ISTI-CNR (costantino.perciante@isti.cnr.it)
+
formPanel.setWidget(panel); //com.google.gwt.user.client.ui.FormPanel
  */
+
fileUpload.setName(...); //com.google.gwt.user.client.ui.FileUpload
public class ClientContextExampleServlet extends RemoteServiceServlet implements GcubeContextExampleServices {
+
  
    private static final long serialVersionUID = -8208421700660483142 L;
 
  
    // Logger
+
// Add hidden parameters
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(ClientContextExampleServlet.class);
+
String currentContextId = GCubeClientContext.getCurrentContextId();
  
    @Override
+
//$YourAttributeName is any attribute name you like, it has to be the same when you'll read it in the servlet's doPost
    public String getInjectedUserid() {
+
panel.add(new Hidden($YourAttributeName, currentContextId)); <-- here is where you put the clientContextId,
  
        // Read user id from the request, using the PortalContext
+
...
        try {
+
</source>
            HttpServletRequest request = this.getThreadLocalRequest();
+
            return PortalContext.getConfiguration().getCurrentUser(request).getUsername();
+
        } catch (Exception e) {
+
            logger.debug("Failed to retrieve the user id", e);
+
        }
+
        return null;
+
    }
+
  
    @Override
+
---
    public String getInjectedContextid() {
+
  
        // Read context id from the request, using the PortalContext
+
<source lang="java">
        try {
+
//server side code
            HttpServletRequest request = this.getThreadLocalRequest();
+
...
            return PortalContext.getConfiguration().getCurrentScope(request);
+
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
         } catch (Exception e) {
+
...
            logger.debug("Failed to retrieve the context id", e);
+
/**
        }
+
* An iterator to instances of <code>FileItemStream</code>
         return null;
+
* parsed from the request, in the order that they were
    }
+
*transmitted.
 +
*/
 +
FileItemIterator fileItemIterator = servletFileUpload.getItemIterator(request);
 +
String clientContextId = "";
 +
//GET FILE STREAM
 +
while (fileItemIterator.hasNext()) {
 +
FileItemStream item = fileItemIterator.next();
 +
         ...
 +
        if (item.isFormField() && $YourAttributeName.equals(item.getFieldName())){ <-- here is where you read the clientContextId in the server  $YourAttributeName must match the hidden value above
 +
        clientContextId = Streams.asString(item.openStream());
 +
}
 +
         ...
  
 +
}
 +
String currentScope= PortalContext.getConfiguration().getCurrentScope(clientContextId));  <-- here  where you get the currentScope from the clientContextId
 +
...
 
}
 
}
 
</source>
 
</source>
  
The result after the methods invocation is this one
 
  
[[File:Contextclientexample_retrievedvalues.png|200px]]
+
=== Case 2 - HTTP GET or POST made with GWT RequestBuilder ===
  
== Tips for Development Environment ==
+
This case is even easier than the Case 1, ClientContext injection for GWT RequestBuilder is already supported, nothing to do client side (we report the client snippet just for clarification). Server side code is the same as in PortalContext
The client context relies on Liferay.ThemeDisplay javascript object, which obviously is not available if you run your application outside the real D4Science's portals. Moreover, the Portal Manager allows to retrieve the current context, the current user (plus some other information, such as his/her current security token) at server side.  To overcome such limitation and allow developers to test their applications in Eclipse, a file like this one can be used:
+
  
<source lang="xml">
+
<source lang="java">
# ONLY FOR LOCAL (IDE) DEVELOPMENT - NOT FOR PRODUCTION USE!
+
# change the properties with your user data and desired scope / token
+
# Author: Massimiliano Assante, CNR-ISTI
+
  
# a development user
+
import com.google.gwt.http.client.*;
user.username=test.user
+
user.name=aTestName
+
user.lastname=aTestLastName
+
user.email=testing.user@gcube-system.org
+
  
# a development scope (the scope must be bound to the token below)
+
//client side code
development.scope=/gcube/devsec/devVRE
+
String url = "http://www.myserver.com/getData?type=3";
 +
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
 +
try {
 +
  Request request = builder.sendRequest(null, new RequestCallback() {
 +
....
 +
...
 +
</source>
  
# a valid user token on the (above) development scope.
+
 
# you can obtain it by registering on one development VRE and using Token Generator portlet
+
<source lang="java">
user.token= ....
+
//server side code
 +
 
 +
import org.gcube.common.portal;
 +
 
 +
public void doGet(HttpServletRequest request, HttpServletResponse response) {
 +
...
 +
PortalContext pContext = PortalContext.getConfiguration();
 +
String currentScope = pContext.getCurrentScope(request);
 +
...
 
</source>
 
</source>
  
 +
== Related links and references ==
 +
 +
[1] Liferay Javascript Object usage: https://web.liferay.com/web/pankaj.kathiriya/blog/-/blogs/usage-of-liferay-js-object
  
The name of the file must be '''gcube-dev-context.properties''', and must be placed under the CATALINA_HOME system variable. The PortalManager will recognize if the application is running on a real or fake portal and, in the latter case, will retrieve and use the information written in this file.  
+
[2] gCube Portal Manager API: https://wiki.gcube-system.org/gcube/Portal_Context
  
'''NOTE''' : depending on the current implementation, the PortalManager set/doesn't set in the ThreadLocal variables the retrieved user's security Token and current Context, once it is queried for this information. ''Be sure to always set them in your own code, if you need them.''
+
[3] gCube UsersManagement Library https://wiki.gcube-system.org/gcube/UserManagement_Core#UserManager_APIs

Latest revision as of 16:44, 25 November 2016

Client Context library for gCube Portlets

Starting from gCube 4.2, the current Context identifier resides client side. If your Portlet does use AJAX calls (i.e. XMLHttpRequest to exchange data with a server behind the scenes) you don't need this component. Everything is already set up for you by the gCube Portal/Gateway in which your application is running.

If you use standard http GET or POST to exchange data with the server instead, and you need to pass to the server the current Infrastructure scope identifier (the Liferay groupId).

Getting the Client Context in Javascript

<script>
var groupId = Liferay.ThemeDisplay.getScopeGroupId();
</script>

Read more about Liferay Javascript Object: [1]

Getting the Client Context in GWT (Widget)

The Client Context has been wrapped as a GWT Widget. Its source code is available in the gCube SVN repository, at this url.

To use the widget, you need to declare the following maven dependency in your project's pom.xml

<dependency>
        <groupId>org.gcube.portal</groupId>
	<artifactId>client-context-library</artifactId>
	<version>[1.0.0-SNAPSHOT,)</version>
	<scope>compile</scope>
</dependency>

as well as the following line in the gwt.xml file of your gwt-portlet:

<!--Inherit the GCubeClientContext widget code -->
<inherits name='org.gcube.portal.clientcontext.GCubeClientContext' />

then you just use the GCubeClientContext.getCurrentContextId() method:

public static native String getCurrentContextId();

Either if you pass this identifier explicitly as parameter, or embed it in the header of your call, in the server you can use the following method to get the infrastructure scope (note: scopeGroupId parameter is the value returned from the GCubeClientContext.getCurrentContextId() method described previously:

import org.gcube.common.portal;
...
PortalContext pContext = PortalContext.getConfiguration();
 
/**
 * 
 * @param scopeGroupId the liferay groupid number (as String) of the VRE/VO
 * @return the scope (context)
 */
String currentScope = pContext.getCurrentScope(String scopeGroupId);

Examples for passing the Client Context via Standard HTTP POST or GET

It can happen that in your web applications you have to use standard HTTP POST or GET for your servlet. This is true, for instance, when you have to upload a file from the browser.

Other cases may involve the usage of the GWT RequestBuilder object (com.google.gwt.http.client.RequestBuilder) for standard HTTP Calls.

For both cases we here suggests to possible solution, in order to pass the ClientContext to your servlets.

Case 1 - HTTP POST requests

A possible approach is to use one (HTML) Hidden field in the form and to read this hidden field value in the doPost servlet's method.

The following example is written in GWT.

//client side code
...
VerticalPanel panel = new VerticalPanel();
formPanel.setWidget(panel); //com.google.gwt.user.client.ui.FormPanel
fileUpload.setName(...);  //com.google.gwt.user.client.ui.FileUpload
 
 
// Add hidden parameters
String currentContextId = GCubeClientContext.getCurrentContextId();
 
//$YourAttributeName is any attribute name you like, it has to be the same when you'll read it in the servlet's doPost
panel.add(new Hidden($YourAttributeName, currentContextId)); <-- here is where you put the clientContextId, 
 
...

---

//server side code
...
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
...
/**
 * An iterator to instances of <code>FileItemStream</code>
 * parsed from the request, in the order that they were
 *transmitted.
 */
FileItemIterator fileItemIterator = servletFileUpload.getItemIterator(request);
String clientContextId = "";
//GET FILE STREAM
while (fileItemIterator.hasNext()) {
	FileItemStream item = fileItemIterator.next();
        ...
        if (item.isFormField() && $YourAttributeName.equals(item.getFieldName())){  <-- here is where you read the clientContextId in the server  $YourAttributeName must match the hidden value above
	         clientContextId = Streams.asString(item.openStream());
	}
        ...
 
}
String currentScope= PortalContext.getConfiguration().getCurrentScope(clientContextId));  <-- here  where you get the currentScope from the clientContextId
...
}


Case 2 - HTTP GET or POST made with GWT RequestBuilder

This case is even easier than the Case 1, ClientContext injection for GWT RequestBuilder is already supported, nothing to do client side (we report the client snippet just for clarification). Server side code is the same as in PortalContext

import com.google.gwt.http.client.*;
 
//client side code
String url = "http://www.myserver.com/getData?type=3";
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(url));
try {
  Request request = builder.sendRequest(null, new RequestCallback() {
....
...


//server side code
 
import org.gcube.common.portal;
 
public void doGet(HttpServletRequest request, HttpServletResponse response) {
...
PortalContext pContext = PortalContext.getConfiguration();
String currentScope = pContext.getCurrentScope(request);
...

Related links and references

[1] Liferay Javascript Object usage: https://web.liferay.com/web/pankaj.kathiriya/blog/-/blogs/usage-of-liferay-js-object

[2] gCube Portal Manager API: https://wiki.gcube-system.org/gcube/Portal_Context

[3] gCube UsersManagement Library https://wiki.gcube-system.org/gcube/UserManagement_Core#UserManager_APIs