Difference between revisions of "Developing gCube Portlets Guide"

From Gcube Wiki
Jump to: navigation, search
(Content for the above files)
(For gCube versions higher or equal than 4.2)
 
(48 intermediate revisions by 3 users not shown)
Line 1: Line 1:
== Development of a gCube Portlet (GWT and Portlet)==
+
<!-- CATEGORIES -->
 +
[[Category:Developer's Guide]]
 +
<!-- END CATEGORIES -->
 +
== Development of a gCube Mavenized Portlet (GWT and Portlet)==
 +
 
 +
=== Preface ===
 +
This guide is about how to create a Liferay portlet using '''GWT''' ('''G'''oogle '''W'''eb '''T'''oolkit) and '''Maven''' on '''Eclipse''' IDE. In principle you could avoid using Maven, but it will make life easier, so let use it. <br>
 +
This guide was tested with Eclipse 4.4 Luna EE (3.7+ is supported), on Ubuntu 14.04 with Java 7.
 +
Official notes on how to combine GWT and Maven are available at the following link https://gwt-maven-plugin.github.io/gwt-maven-plugin/.
  
 
=== Prerequisites ===
 
=== Prerequisites ===
  
* IDE: Eclipse 3.4+ J2EE with the following plugins installed
+
* '''IDE''': Eclipse Java EE IDE for Web Developers. Version: 3.7+
** Portal Pack 2.0.1 [PPAC] [https://gcube.wiki.gcube-system.org/gcube/images_gcube/e/ed/Eclipse-portal-pack-2.0.1.tar.gz get PPAC Eclipse Plugin]. The jars must be copied to the eclipse's plugin directory (Please Note: such plugin is already bundled with Eclipse 3.7 Indigo EE for Java developers)
+
* '''Google Eclipse Plugin''' [GECLIPSE] (If you want your portlet to use GWT) [https://developers.google.com/eclipse/docs/getting_started GECLIPSE]
** Google Eclipse Plugin [GECLIPSE] (If you want your portlet to use GWT) [http://code.google.com/webtoolkit/download.html get GECLIPSE]
+
** GWT 2.7.0 SDK (Bundled with the above Plugin)
** GWT 2.4.0 (Bundled with the above Plugin)
+
* '''m2e''' – maven integration for eclipse available [http://download.eclipse.org/technology/m2e/releases here]
 +
* '''m2e-wtp''' – maven Integration for WTP available [http://download.eclipse.org/m2e-wtp/releases/ here]
  
=== Creating an empty Portlet Project ===
+
=== Project creation ===
 +
Create a new maven project, when prompted for selecting the archetype type 'org.codehaus.mojo' and select the gwt-maven-plugin as shown in the picture below (select 2.7.0 if you use GWT 2.7):
  
* Create a new Dynamic Web Project using eclipse
+
[[File:Project_creation_mojo.png|center|500px]]
  
[[File:NewDWP.jpg]]
+
Click next and enter the maven coordinates for your artifact, see below for an example:
  
* In the Wizard next step we are going to add support for Porlet 2.0. and specify Tomcat 5.5 as Target Runtime (Optional)
+
[[File:Entry_point_maven_coordinate_definitions.png|center|500px]]<br>
  
[[File:NewPorlet.jpg]]
+
'''Sample''' is going to be the entry point class of your gwt application. <br>
  
* Finally in the Wizard last step we change "Web Content" to "war" as Content Directory (To have ETICS and GWT2 Projects compatibility)
+
===Writing a GWT application===
 +
You can find useful information about how to write a gwt application on the GWT's official site: [http://code.google.com/webtoolkit/ http://code.google.com/webtoolkit/].<br>
  
=== Portlet creation ===
+
====<span style="color:red;">! IMPORTANT INFO</span>====
 +
In the entrypoint-class, in the onModuleLoad() method:
 +
When you are about to add your widget in the main page,<br>
 +
instead of writing:
 +
<source lang="java">RootPanel.get().add(<your widget>);
 +
</source>
 +
you must write:
 +
<source lang="java">RootPanel.get(<a unique id for the DIV>).add(<your widget>);
 +
</source>
  
To create your portlet using eclipse, click on your newly created project > New -> Other -> JavaPortlet.
+
So, the generated html will be placed in the predefined ''div'' instead of being placed in the body of the HTML page.
  
[[File:NewPortlet.jpg]]
+
=== Project First Run ===
 +
Now you are ready to see it working! However, if you try to run it as Web Application, you will be warned about the fact that the web.xml file is not complete
  
You are also required to change your project default output folder from <MyGCubePortletName>/build/classes to <MyGCubePortletName>/war/WEB-INF/classes
+
<source lang="xml">
 +
Loading modules
 +
  org.gcube.portlet.user.sample_portlet.*
 +
      Validating <servlet> tags for module 'Sample'
 +
        [WARN] Module declares a servlet class 'org.gcube.portlet.user.sample_portlet.server.GreetingServiceImpl
 +
with a mapping to '/Sample/Sample/greet', but the web.xml has no corresponding mapping;
 +
please add the following lines to your web.xml:
 +
<servlet-mapping>
 +
  <servlet-name>greetServlet</servlet-name>
 +
  <url-pattern>/Sample/Sample/greet</url-pattern>
 +
</servlet-mapping>
 +
</source>
  
[[File:Outputfolder.jpg]]
+
Fix the web.xml file as specified above and retry, now your browser should start and you can try your first GWT mavenized application.
  
If you performed the above procedure correctly now your project should look similar to the picture below:
+
[[File:First_application.png|center|400px]]
  
[[File:MyGcubeProject.jpg]]
+
=== Adapting your Portlet to Liferay Portal ===
  
----
+
gCube as chosen to move to Liferay Portal from its 1.9 release.
You have created a Standard Portlet Project, to generate a war simple use the Eclipse Export > War Wizard
+
In order to make your portlet be deployable on Liferay you need to add some Liferay Portal specific deployment descriptor xml and property files (In addition to the standard one for portlets, the '''portlet.xml'''). Moreover, you need to create a java class file that extends Liferay's '''GenericPortlet'''.
  
=== Adapting your Portlet to Liferay Portal ===
+
Suppose '''MyGCubePortletName''' is your portlet's name. Create a java class named MyGCubePortletNamePortlet whose content is the following:
  
D4Science II Project as chosen to move to Liferay Portal from its 1.9 release.  
+
<source lang="java">
In order to make your portlet be deployable on Liferay you need to add some Liferay Portal specific deployment descriptor xml and property files. (In addition to the standard one for portlets, the portlet.xml)
+
public class MyGCubePortletNamePortlet extends GenericPortlet {
 +
public void doView(RenderRequest request, RenderResponse response)throws PortletException, IOException {
 +
    response.setContentType("text/html");
 +
            PortalContext.setUserInSession(request); //needed only if you have custom servlet that needs to know the current user in your war
 +
    PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher("/WEB-INF/jsp/MyGCubePortletNamePortlet_view.jsp");
 +
    dispatcher.include(request, response);
 +
}
 +
public void processAction(ActionRequest request, ActionResponse response)
 +
throws PortletException, IOException {}
 +
}
 +
</source>
  
Create two empty xml files into your project war WEB-INF folder and name them
+
To access the GenericPortlet add the following dependency to your pom.xml
 +
<source lang="xml">
 +
<dependency>
 +
<groupId>javax.portlet</groupId>
 +
        <artifactId>portlet-api</artifactId>
 +
<scope>provided</scope>
 +
</dependency>
 +
</source>
  
* liferay-portlet.xml
+
We are going to create some other files, including "MyGCubePortletNamePortlet_view.jsp".
* liferay-display.xml
+
Move into the directory '''src/main/webapps/WEB-INF''', in which the web.xml file is located too. Here you need to:
  
Create 1 empty property file into your project war WEB-INF folder and name it
+
*Create two empty xml files and name them
 +
** '''liferay-portlet.xml'''
 +
** '''liferay-display.xml'''
  
* liferay-plugin-package.properties
+
*Create 1 empty property file into your project war WEB-INF folder and name it
 +
** '''liferay-plugin-package.properties'''
 +
 
 +
* Create a directory named '''jsp'''
 +
** add the file '''MyGCubePortletName_view.jsp''' inside the just create directory.
  
 
==== Content for the above files ====
 
==== Content for the above files ====
<MyGCubePortletName> has to be changed with your actual portlet name
+
 
 +
* <b>portlet.xml</b>
 +
This file could contain more than one '''<portlet>''' tag, in this case we suppose there is only one
 +
<source lang="xml">
 +
<?xml version="1.0"?>
 +
<portlet-app
 +
version="2.0"
 +
xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
 +
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 +
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
 +
        http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
 +
<portlet>
 +
<portlet-name>MyGCubePortletName</portlet-name>
 +
<display-name>MyGCubePortletName</display-name>
 +
<portlet-class>MyGCubePortletName</portlet-class>
 +
<init-param>
 +
<name>view-jsp</name>
 +
<value>/view.jsp</value>
 +
</init-param>
 +
<expiration-cache>0</expiration-cache>
 +
<supports>
 +
<mime-type>text/html</mime-type>
 +
</supports>
 +
<portlet-info>
 +
<title>Share updates</title>
 +
<short-title>Share</short-title>
 +
<keywords>Share Updates</keywords>
 +
</portlet-info>
 +
<security-role-ref>
 +
<role-name>administrator</role-name>
 +
</security-role-ref>
 +
</portlet>
 +
</portlet-app>
 +
</source>
  
 
* <b>liferay-portlet.xml</b>
 
* <b>liferay-portlet.xml</b>
 
The elements that should be placed in that file must follow the exact order
 
The elements that should be placed in that file must follow the exact order
...
 
 
...
 
...
 
<source lang="xml">
 
<source lang="xml">
 
<?xml version="1.0"?>
 
<?xml version="1.0"?>
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 5.2.0//EN" "http://www.liferay.com/dtd/liferay-portlet-app_5_2_0.dtd">
+
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 6.2.0//EN"  
 +
  "http://www.liferay.com/dtd/liferay-portlet-app_6_2_0.dtd">
 
<liferay-portlet-app>
 
<liferay-portlet-app>
 
<portlet>
 
<portlet>
<portlet-name><MyGCubePortletName></portlet-name>
+
<portlet-name>MyGCubePortletName</portlet-name>
<layout-cacheable>false</layout-cacheable> <b>//for loading the new portlet</b>
+
<layout-cacheable>false</layout-cacheable>
<instanceable>false</instanceable>    <b>//to use one portlet instance for the D4S portal</b>
+
<instanceable>false</instanceable>
<ajaxable>false</ajaxable>  <b>//it is need for the correct rendering of the GWT portlet</b>
+
<ajaxable>false</ajaxable>
<!-- LOCATION CSS HERE -->
+
                <!-- LOCATION CSS HERE -->
                <header-portlet-css>/css/test.css</header-portlet-css>
+
<header-portlet-css>/MyGCubePortletName.css</header-portlet-css>
 
</portlet>
 
</portlet>
 
<role-mapper>
 
<role-mapper>
Line 76: Line 163:
 
</liferay-portlet-app>
 
</liferay-portlet-app>
 
</source>
 
</source>
...
 
 
...
 
...
 
----
 
----
Line 85: Line 171:
 
<source lang="xml">
 
<source lang="xml">
 
<?xml version="1.0"?>
 
<?xml version="1.0"?>
<!DOCTYPE display PUBLIC "-//Liferay//DTD Display 5.2.0//EN" "http://www.liferay.com/dtd/liferay-display_5_2_0.dtd">
+
<!DOCTYPE display PUBLIC "-//Liferay//DTD Display 6.2.0//EN"  
  <display>
+
"http://www.liferay.com/dtd/liferay-display_6_2_0.dtd">
      <category name="gCube Applications">
+
<display>
        <portlet id="<MyGCubePortletName>" />
+
<category name="gCube Applications">
      </category>
+
<portlet id="MyGCubePortletName" />
  </display>
+
</category>
 +
</display>
 
</source>
 
</source>
 
----
 
----
Line 97: Line 184:
  
 
<source lang="xml">
 
<source lang="xml">
name=<MyGCubePortletName>
+
name=MyGCubePortletName
 
module-group-id=liferay
 
module-group-id=liferay
 
module-incremental-version=1
 
module-incremental-version=1
Line 104: Line 191:
 
change-log=
 
change-log=
 
page-url=http://www.d4science.org
 
page-url=http://www.d4science.org
author=D4Science Org
+
author= Author's name
 
licenses=EUPL
 
licenses=EUPL
</source>
+
</source> <br>
  
----
+
* '''JSP file's content'''
If you performed the above procedure correctly now your project should look similar to the picture below:
+
In your GWT Project html file seek the following part:
 
+
<source lang="xml">
[[File:DeploymentDescriptors.jpg]]
+
 
+
=== Download this sample project ===
+
 
+
You can download this sample project complete with samples for the above files from the link below
+
 
+
[[File:MyGCubePortlet.tar.gz]]
+
 
+
The same project is available on the project's svn repository at this URL: http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/portal/portal-framework/MyGCubePortlet/
+
----
+
 
+
===Writing a GWT application===
+
You can find useful information about how to write a gwt application on the '''G'''oogle '''W'''eb '''T'''oolkit's official site: [http://code.google.com/webtoolkit/ http://code.google.com/webtoolkit/].<br>
+
<br>
+
====<span style="color:red;">! IMPORTANT INFO</span>====
+
In the entrypoint-class, in the onModuleLoad() method:
+
When you are about to add your widget in the main page,<br>
+
instead of writing:
+
<source lang="java5">RootPanel.get().add(<your widget>);
+
</source>
+
you must write:
+
<source lang="java5">RootPanel.get(<a unique id for the DIV>).add(<your widget>);
+
</source>
+
 
+
So, the generated html will be placed in the predefined ''div'' instead of being placed in an unspecified location.
+
 
+
=== Integrate a GWT2 Web Application Project ===
+
 
+
Adapting your GWT2 Project is now a straightforward procedure. (We assume you have the GWT Eclipse Plugin Installed)
+
 
+
To add GWT support to your project go to your Project Properties: Google -> Web Toolkit -> Use Google Web Toolkit
+
 
+
[[File:GWTUse.jpg]]
+
 
+
----
+
 
+
Now you have to copy some files from the GWT Project to the portlet project:
+
 
+
* '''copy your GWT src packages into your yet created Portlet Project '''
+
* '''copy your GWT war folder files into your war folder of the yet created Portlet Project ''' e.g. .html,.css  and the other folders of the war, except for WEB-INF
+
* '''Copy your GWT WEB-INF/web.xml file into your portlet war/WEB-INF/web.xml
+
 
+
You also have to edit the following files:
+
 
+
* In your GWT Project html file seek the following part:
+
 
+
<pre>
+
 
<!--                                          -->
 
<!--                                          -->
 
     <!-- This script loads your compiled module.  -->
 
     <!-- This script loads your compiled module.  -->
Line 164: Line 204:
 
     <!--                                          -->
 
     <!--                                          -->
 
     <script type="text/javascript" language="javascript" src="mygwtproject/mygwtproject.nocache.js"></script>
 
     <script type="text/javascript" language="javascript" src="mygwtproject/mygwtproject.nocache.js"></script>
</pre>
+
</source>
  
* Open your ./war/WEB-INF/jsp/LoginPortlet_view.jsp file and add the content of the <script> element above as shown below  
+
Open your .jsp file and add the content of the <script> element above as shown below. Notice that request.getContextPath is added, together with the '''div''' using as id the unique name you specified in your entrypoint class
** Notice that request.getContextPath is added, together with the '''div''' using as id the unique name you specified in your entrypoint class
+
  
<pre>
+
<source lang="xml">
 
<portlet:defineObjects />
 
<portlet:defineObjects />
 
--%>
 
--%>
 
<script type="text/javascript" language="javascript" src='<%=request.getContextPath()%>/mygwtproject/mygwtproject.nocache.js'></script>
 
<script type="text/javascript" language="javascript" src='<%=request.getContextPath()%>/mygwtproject/mygwtproject.nocache.js'></script>
 
 
<div id="a unique id for the DIV">
 
<div id="a unique id for the DIV">
 
</div>
 
</div>
  
</pre>
+
</source>
  
----
+
If you performed the above procedure correctly now your project should look similar to the picture below:
  
* The CSS file Location of your GWT application must be set into the <header-portlet-css> element of the liferay-portlet.xml file as shown below (mygwtproject.css is in the war/ folder in this example)
+
[[File:Project_structure.png|center|500px]]
<pre>
+
<liferay-portlet-app>
+
<portlet>
+
<portlet-name>WebLogin</portlet-name>
+
<icon>/icon.png</icon>
+
<layout-cacheable>false</layout-cacheable>
+
<instanceable>false</instanceable>
+
<ajaxable>false</ajaxable>
+
<!-- LOCATION CSS HERE -->
+
                <header-portlet-css>/mygwtproject.css</header-portlet-css>
+
</portlet>
+
<role-mapper>
+
<role-name>administrator</role-name>
+
<role-link>Administrator</role-link>
+
</role-mapper>
+
</liferay-portlet-app>
+
</pre>
+
  
----
+
=== Maven Goals (Create your Web Archive) ===
  
 +
* goal gwt:compile
 +
** GWT Compile mode In order to gwt:compile your portlet create a Maven Build Configuration and enter gwt:compile on the goal text box.
  
=== Compile GWT Inside the Portlet Project ===
+
* goal package
 +
**package will generate the war for you in the target folder.
  
'''You can now compile your GWT Application (Inside the portlet Project) by using the Eclipse Plugin''' as shown below
+
* goal deploy
 +
** to use the deploy goal please refere to the gCube Maven Guide as you would need to create a distro folder with specific files in it.
  
 +
=== Download the sample project ===
  
 +
You can download a sample project complete with samples for the above files from the link below
  
[[File:GWTCompile.jpg]]
+
[[File:SamplePortletProject.tar.gz]]<br>
  
 +
Or you can download the source code from the following link (from gCube SVN Repository)
  
----
+
[http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/portlets/user/gCubeSamplePortlet/ Sample Project SVN Link]
  
 
== Set the Portlet Context (How to get the logged in user's information) ==
 
== Set the Portlet Context (How to get the logged in user's information) ==
  
In order to get a GCube Portlet work into the portal you need to pass it the context in which the portelt is running into.
+
=== For gCube versions lower than gCube 4.2 ===
 
+
In order to get a GCube Portlet work into the portal you need to pass it the context in which the portlet is running into. In fact, we add the
To do so:
+
org.gcube.portal.custom-portal-handler module dependency to our pom.xml file project.
Get the latest version of the module org.gcube.portal.custom-portal-handler from ETICS.
+
  
 
CustomPortalHandler set the portlet context transparently to the developer (no need to call Liferay API as used to do in the past).  
 
CustomPortalHandler set the portlet context transparently to the developer (no need to call Liferay API as used to do in the past).  
 
Instead of asking for the username, it sets that in the session. So that the new doView() method will look like:
 
Instead of asking for the username, it sets that in the session. So that the new doView() method will look like:
  
<source lang="java5">
+
<source lang="java">
 
public void doView(RenderRequest request, RenderResponse response)    throws PortletException, IOException {
 
public void doView(RenderRequest request, RenderResponse response)    throws PortletException, IOException {
     ScopeHelper.setContext(request); // <-- Static method which sets the username in the session and the scope depending on the context automatically
+
     ScopeHelper.setContext(request); // <-- Static method which sets the context in the session (user, scope, token) automatically
 
     PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher(....);
 
     PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher(....);
 
     dispatcher.include(request, response);
 
     dispatcher.include(request, response);
Line 232: Line 259:
 
</source>
 
</source>
  
Moreover, from within the GWT servlets you can get the current username from the http session using the ScopeHelper.USERNAME_ATTRIBUTE
+
Moreover, from within the GWT servlets you can get the current username from the http session using the ScopeHelper.USERNAME_ATTRIBUTE
 
e.g.
 
e.g.
  
<source lang="java5">
+
<source lang="java">
 
HTTPSession httpSession = this.getThreadLocalRequest().getSession();
 
HTTPSession httpSession = this.getThreadLocalRequest().getSession();
 
String sessionID = httpSession.getId();
 
String sessionID = httpSession.getId();
 
String username =  httpSession.getAttribute(ScopeHelper.USERNAME_ATTRIBUTE).toString();
 
String username =  httpSession.getAttribute(ScopeHelper.USERNAME_ATTRIBUTE).toString();
 
</source>
 
</source>
----
 
  
=== Create a Web Archive from Portlet Project ===
+
=== For gCube versions higher or equal than 4.2 ===
 +
ScopeHelper.setContext is no longer needed, a new way of performing this operation (plus some others) is available starting from this release. See [https://wiki.gcube-system.org/gcube/Portal_Context Portal Context] for more information.
  
'''You can now create the Web Application Archive (WAR) using the export feature of Eclipse'''
+
<source lang="java">
 +
public void doView(RenderRequest request, RenderResponse response)   throws PortletException, IOException {
 +
    PortalContext.setUserInSession(request); //needed only if you have custom servlet that needs to know the current user in your war
 +
    PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher(....);
 +
    dispatcher.include(request, response);
 +
}
 +
</source>
  
 +
== Migrating from Liferay 6.0 to Liferay 6.2.5 ==
  
[[File:War.jpg]] [[File:War2.jpg]]
+
=== Main changes to deploy portlets ===
 +
Starting from gCube 4.0.0, Liferay 6.2.5 will be used. In order to deploy portlets on the new version, some changes have to be done. If you were using an old Liferay version, please check and change if needed the following files (location of these files is in your project src/main/webapp/WEB-INF folder):
 +
 
 +
* DTD declaration into '''liferay-display.xml''' replace with
 +
<source lang="xml">
 +
<!DOCTYPE display PUBLIC "-//Liferay//DTD Display 6.2.0//EN"
 +
  "http://www.liferay.com/dtd/liferay-display_6_2_0.dtd">
 +
</source>
 +
 
 +
* DTD declaration into '''liferay-portlet.xml''' replace with
 +
<source lang="xml">
 +
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 6.2.0//EN"
 +
  "http://www.liferay.com/dtd/liferay-portlet-app_6_2_0.dtd">
 +
</source>
 +
 
 +
* <web-app> tag into '''web.xml''' replace with
 +
<source lang="xml">
 +
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 +
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
 +
          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
 +
version="3.0">
 +
</source>
 +
 
 +
=== UserManagement Library 2.0 ===
 +
 
 +
The UserManagement library '''must be''' used to manage users, groups (VREs) and roles. You are discouraged to use Liferay's API directly, because of different abstractions made upon such concepts by the gCube framework.
 +
 
 +
The library offers APIs to manage Liferay's Users, Roles and Groups using the associated managers (i.e., to manage users you need to instanciate
 +
the UserManager and so on).
 +
 
 +
With respect the previous version of the library:
 +
* VRE, VO e RootVO are no longer modelled as Organisations in Liferay, rather they are modelled as Sites;
 +
* '''GCubeUser''' (replaces UserModel), '''GCubeGroup''' (replaces GroupModel), '''GCubeRole''' replaces (RoleModel) now contain Liferay's User, Group and Role information respectively;
 +
* GCubeTeam has been introduced to support something similar to a Role, but restricted to a particular VRE. A role, instead, is available in any VRE.
 +
 
 +
For more information and examples look at the [https://wiki.gcube-system.org/gcube/UserManagement_Core dedicated page].
 +
 
 +
=== PortalManager 2.0 ===
 +
The gCube Portal Manager is a component all portlets need to use to get the context where they are running. The new version 2.0 offers other facilities. See [https://wiki.gcube-system.org/gcube/Portal_Manager here] for more details.
 +
 
 +
=== ASL Social 1.0 ===
 +
 
 +
The ApplicationSupportLayer is a library that uses the SocialNetworkingLibrary one to offer services to portlets (e.g. notification mechanisms).
 +
 
 +
The new version has a main change into the ApplicationNotificationsManager: it no longer requires the AslSession.
 +
 
 +
The new constructor requires :
 +
*SocialNetworkingSite site filled with the required data
 +
* String scope; // the current scope
 +
* SocialNetworkingUser currUser. // the current user
 +
 
 +
see also: [https://wiki.gcube-system.org/gcube/Social_Networking_Library#User_Notifications_Manager ASL Notifications examples]
  
 
== Troubleshooting ==
 
== Troubleshooting ==
Line 259: Line 344:
 
</pre>
 
</pre>
  
''' Cause: ''' : Tomcat classpath is listed before your GWT classpath
+
''' Cause ''' : Tomcat classpath is listed before your GWT classpath
 +
 
 +
''' Solution ''' : Go to your Project Properties > Order and Export and make sure GWT SDK is listed before Tomcat one
 +
 
 +
* While deploying the generated .war in Liferay, you get
 +
<pre>
 +
ERROR [MinifierFilter:136] java.lang.NullPointerException
 +
</pre>
 +
'''Solution(s)''' : Open your .gwt.xml, the rename-to attribute value of <module> for some unknown reason must be lowercase. If the above didn't work also having the same gwt-module name and the portlet class name would make the Minifier crashing.If none of the above worked also having the same gwt-module name and portlet id name (in WEB-INF xml files) would make the Minifier crashing.
 +
 
 +
* When moving to Liferay 6.2.5 you need to update the web.xml's <web-app> tag. After having changed it, Eclipse gives this error
 +
<pre>
 +
Description Resource Path Location Type Cannot change version of project facet Dynamic Web Module to 3.0.
 +
</pre>
  
''' Solution: ''' : Go to your Project Properties > Order and Export and make sure GWT SDK is listed before Tomcat one
+
'''Solution''': go to Window > Show View > Navigator, under the .settings directory of the project, there is a file named org.eclipse.wst.common.project.facet.core.xml, find the jst.web facet and change it to version 3.0. Now make a maven update and the error should be fixed.

Latest revision as of 13:00, 18 January 2017

Development of a gCube Mavenized Portlet (GWT and Portlet)

Preface

This guide is about how to create a Liferay portlet using GWT (Google Web Toolkit) and Maven on Eclipse IDE. In principle you could avoid using Maven, but it will make life easier, so let use it.
This guide was tested with Eclipse 4.4 Luna EE (3.7+ is supported), on Ubuntu 14.04 with Java 7. Official notes on how to combine GWT and Maven are available at the following link https://gwt-maven-plugin.github.io/gwt-maven-plugin/.

Prerequisites

  • IDE: Eclipse Java EE IDE for Web Developers. Version: 3.7+
  • Google Eclipse Plugin [GECLIPSE] (If you want your portlet to use GWT) GECLIPSE
    • GWT 2.7.0 SDK (Bundled with the above Plugin)
  • m2e – maven integration for eclipse available here
  • m2e-wtp – maven Integration for WTP available here

Project creation

Create a new maven project, when prompted for selecting the archetype type 'org.codehaus.mojo' and select the gwt-maven-plugin as shown in the picture below (select 2.7.0 if you use GWT 2.7):

Project creation mojo.png

Click next and enter the maven coordinates for your artifact, see below for an example:

Entry point maven coordinate definitions.png

Sample is going to be the entry point class of your gwt application.

Writing a GWT application

You can find useful information about how to write a gwt application on the GWT's official site: http://code.google.com/webtoolkit/.

! IMPORTANT INFO

In the entrypoint-class, in the onModuleLoad() method: When you are about to add your widget in the main page,
instead of writing:

RootPanel.get().add(<your widget>);

you must write:

RootPanel.get(<a unique id for the DIV>).add(<your widget>);

So, the generated html will be placed in the predefined div instead of being placed in the body of the HTML page.

Project First Run

Now you are ready to see it working! However, if you try to run it as Web Application, you will be warned about the fact that the web.xml file is not complete

Loading modules
   org.gcube.portlet.user.sample_portlet.*
      Validating <servlet> tags for module 'Sample'
         [WARN] Module declares a servlet class 'org.gcube.portlet.user.sample_portlet.server.GreetingServiceImpl 
with a mapping to '/Sample/Sample/greet', but the web.xml has no corresponding mapping;
please add the following lines to your web.xml:
<servlet-mapping>
  <servlet-name>greetServlet</servlet-name>
  <url-pattern>/Sample/Sample/greet</url-pattern>
</servlet-mapping>

Fix the web.xml file as specified above and retry, now your browser should start and you can try your first GWT mavenized application.

First application.png

Adapting your Portlet to Liferay Portal

gCube as chosen to move to Liferay Portal from its 1.9 release. In order to make your portlet be deployable on Liferay you need to add some Liferay Portal specific deployment descriptor xml and property files (In addition to the standard one for portlets, the portlet.xml). Moreover, you need to create a java class file that extends Liferay's GenericPortlet.

Suppose MyGCubePortletName is your portlet's name. Create a java class named MyGCubePortletNamePortlet whose content is the following:

 
public class MyGCubePortletNamePortlet extends GenericPortlet {
	public void doView(RenderRequest request, RenderResponse response)throws PortletException, IOException {
	    response.setContentType("text/html");
            PortalContext.setUserInSession(request); //needed only if you have custom servlet that needs to know the current user in your war
	    PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher("/WEB-INF/jsp/MyGCubePortletNamePortlet_view.jsp");
	    dispatcher.include(request, response);		
	}
	public void processAction(ActionRequest request, ActionResponse response)
			throws PortletException, IOException {}
}

To access the GenericPortlet add the following dependency to your pom.xml

<dependency>
	<groupId>javax.portlet</groupId>
        <artifactId>portlet-api</artifactId>
	<scope>provided</scope>
</dependency>

We are going to create some other files, including "MyGCubePortletNamePortlet_view.jsp". Move into the directory src/main/webapps/WEB-INF, in which the web.xml file is located too. Here you need to:

  • Create two empty xml files and name them
    • liferay-portlet.xml
    • liferay-display.xml
  • Create 1 empty property file into your project war WEB-INF folder and name it
    • liferay-plugin-package.properties
  • Create a directory named jsp
    • add the file MyGCubePortletName_view.jsp inside the just create directory.

Content for the above files

  • portlet.xml

This file could contain more than one <portlet> tag, in this case we suppose there is only one

<?xml version="1.0"?>
<portlet-app
	version="2.0"
	xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd 
        http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
	<portlet>
		<portlet-name>MyGCubePortletName</portlet-name>
		<display-name>MyGCubePortletName</display-name>
		<portlet-class>MyGCubePortletName</portlet-class>
		<init-param>
			<name>view-jsp</name>
			<value>/view.jsp</value>
		</init-param>
		<expiration-cache>0</expiration-cache>
		<supports>
			<mime-type>text/html</mime-type>
		</supports>
		<portlet-info>
			<title>Share updates</title>
			<short-title>Share</short-title>
			<keywords>Share Updates</keywords>
		</portlet-info>
		<security-role-ref>
			<role-name>administrator</role-name>
		</security-role-ref>
	</portlet>
</portlet-app>
  • liferay-portlet.xml

The elements that should be placed in that file must follow the exact order ...

<?xml version="1.0"?>
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 6.2.0//EN" 
  "http://www.liferay.com/dtd/liferay-portlet-app_6_2_0.dtd">
<liferay-portlet-app>
	<portlet>
		<portlet-name>MyGCubePortletName</portlet-name>
		<layout-cacheable>false</layout-cacheable>
		<instanceable>false</instanceable>
		<ajaxable>false</ajaxable>
                <!-- LOCATION CSS HERE -->
		<header-portlet-css>/MyGCubePortletName.css</header-portlet-css>
	</portlet>
	<role-mapper>
		<role-name>administrator</role-name>
		<role-link>Administrator</role-link>
	</role-mapper>
</liferay-portlet-app>

...


  • liferay-display.xml

The element 'category' is recommended to use the name "gCube Applications"

<?xml version="1.0"?>
<!DOCTYPE display PUBLIC "-//Liferay//DTD Display 6.2.0//EN" 
 "http://www.liferay.com/dtd/liferay-display_6_2_0.dtd">
<display>
	<category name="gCube Applications">
		<portlet id="MyGCubePortletName" />
	</category>
</display>

  • liferay-plugin-package.properties
name=MyGCubePortletName
module-group-id=liferay
module-incremental-version=1
tags=
short-description=
change-log=
page-url=http://www.d4science.org
author= Author's name
licenses=EUPL

  • JSP file's content

In your GWT Project html file seek the following part:

<!--                                           -->
    <!-- This script loads your compiled module.   -->
    <!-- If you add any GWT meta tags, they must   -->
    <!-- be added before this line.                -->
    <!--                                           -->
    <script type="text/javascript" language="javascript" src="mygwtproject/mygwtproject.nocache.js"></script>

Open your .jsp file and add the content of the <script> element above as shown below. Notice that request.getContextPath is added, together with the div using as id the unique name you specified in your entrypoint class

<portlet:defineObjects />
--%>
<script type="text/javascript" language="javascript" src='<%=request.getContextPath()%>/mygwtproject/mygwtproject.nocache.js'></script>
<div id="a unique id for the DIV">
</div>

If you performed the above procedure correctly now your project should look similar to the picture below:

Project structure.png

Maven Goals (Create your Web Archive)

  • goal gwt:compile
    • GWT Compile mode In order to gwt:compile your portlet create a Maven Build Configuration and enter gwt:compile on the goal text box.
  • goal package
    • package will generate the war for you in the target folder.
  • goal deploy
    • to use the deploy goal please refere to the gCube Maven Guide as you would need to create a distro folder with specific files in it.

Download the sample project

You can download a sample project complete with samples for the above files from the link below

File:SamplePortletProject.tar.gz

Or you can download the source code from the following link (from gCube SVN Repository)

Sample Project SVN Link

Set the Portlet Context (How to get the logged in user's information)

For gCube versions lower than gCube 4.2

In order to get a GCube Portlet work into the portal you need to pass it the context in which the portlet is running into. In fact, we add the org.gcube.portal.custom-portal-handler module dependency to our pom.xml file project.

CustomPortalHandler set the portlet context transparently to the developer (no need to call Liferay API as used to do in the past). Instead of asking for the username, it sets that in the session. So that the new doView() method will look like:

public void doView(RenderRequest request, RenderResponse response)    throws PortletException, IOException {
    ScopeHelper.setContext(request); // <-- Static method which sets the context in the session (user, scope, token) automatically
    PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher(....);
    dispatcher.include(request, response);
}

Moreover, from within the GWT servlets you can get the current username from the http session using the ScopeHelper.USERNAME_ATTRIBUTE e.g.

HTTPSession httpSession = this.getThreadLocalRequest().getSession();
String sessionID = httpSession.getId();
String username =  httpSession.getAttribute(ScopeHelper.USERNAME_ATTRIBUTE).toString();

For gCube versions higher or equal than 4.2

ScopeHelper.setContext is no longer needed, a new way of performing this operation (plus some others) is available starting from this release. See Portal Context for more information.

public void doView(RenderRequest request, RenderResponse response)    throws PortletException, IOException {
    PortalContext.setUserInSession(request); //needed only if you have custom servlet that needs to know the current user in your war
    PortletRequestDispatcher dispatcher = getPortletContext().getRequestDispatcher(....);
    dispatcher.include(request, response);
}

Migrating from Liferay 6.0 to Liferay 6.2.5

Main changes to deploy portlets

Starting from gCube 4.0.0, Liferay 6.2.5 will be used. In order to deploy portlets on the new version, some changes have to be done. If you were using an old Liferay version, please check and change if needed the following files (location of these files is in your project src/main/webapp/WEB-INF folder):

  • DTD declaration into liferay-display.xml replace with
<!DOCTYPE display PUBLIC "-//Liferay//DTD Display 6.2.0//EN" 
  "http://www.liferay.com/dtd/liferay-display_6_2_0.dtd">
  • DTD declaration into liferay-portlet.xml replace with
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 6.2.0//EN" 
  "http://www.liferay.com/dtd/liferay-portlet-app_6_2_0.dtd">
  • <web-app> tag into web.xml replace with
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
          http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">

UserManagement Library 2.0

The UserManagement library must be used to manage users, groups (VREs) and roles. You are discouraged to use Liferay's API directly, because of different abstractions made upon such concepts by the gCube framework.

The library offers APIs to manage Liferay's Users, Roles and Groups using the associated managers (i.e., to manage users you need to instanciate the UserManager and so on).

With respect the previous version of the library:

  • VRE, VO e RootVO are no longer modelled as Organisations in Liferay, rather they are modelled as Sites;
  • GCubeUser (replaces UserModel), GCubeGroup (replaces GroupModel), GCubeRole replaces (RoleModel) now contain Liferay's User, Group and Role information respectively;
  • GCubeTeam has been introduced to support something similar to a Role, but restricted to a particular VRE. A role, instead, is available in any VRE.

For more information and examples look at the dedicated page.

PortalManager 2.0

The gCube Portal Manager is a component all portlets need to use to get the context where they are running. The new version 2.0 offers other facilities. See here for more details.

ASL Social 1.0

The ApplicationSupportLayer is a library that uses the SocialNetworkingLibrary one to offer services to portlets (e.g. notification mechanisms).

The new version has a main change into the ApplicationNotificationsManager: it no longer requires the AslSession.

The new constructor requires :

  • SocialNetworkingSite site filled with the required data
  • String scope; // the current scope
  • SocialNetworkingUser currUser. // the current user

see also: ASL Notifications examples

Troubleshooting

General issues encountered by developers

  • Once you try to compile your Dynamic Web Project GWT2 Powered you get the following exception
[ERROR] Unexpected
java.lang.NoSuchFieldError: reportUnusedDeclaredThrownExceptionIncludeDocCommentReference

Cause  : Tomcat classpath is listed before your GWT classpath

Solution  : Go to your Project Properties > Order and Export and make sure GWT SDK is listed before Tomcat one

  • While deploying the generated .war in Liferay, you get
ERROR [MinifierFilter:136] java.lang.NullPointerException

Solution(s) : Open your .gwt.xml, the rename-to attribute value of <module> for some unknown reason must be lowercase. If the above didn't work also having the same gwt-module name and the portlet class name would make the Minifier crashing.If none of the above worked also having the same gwt-module name and portlet id name (in WEB-INF xml files) would make the Minifier crashing.

  • When moving to Liferay 6.2.5 you need to update the web.xml's <web-app> tag. After having changed it, Eclipse gives this error
Description Resource Path Location Type Cannot change version of project facet Dynamic Web Module to 3.0.

Solution: go to Window > Show View > Navigator, under the .settings directory of the project, there is a file named org.eclipse.wst.common.project.facet.core.xml, find the jst.web facet and change it to version 3.0. Now make a maven update and the error should be fixed.