Difference between revisions of "Developing gCube Portlets Guide"
Line 2: | Line 2: | ||
[[Category:Developer's Guide]] | [[Category:Developer's Guide]] | ||
<!-- END CATEGORIES --> | <!-- END CATEGORIES --> | ||
− | == Development of a gCube Portlet (GWT and Portlet)== | + | == 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 Java EE IDE for Web Developers. Version: 3.7+ | + | * '''IDE''': Eclipse Java EE IDE for Web Developers. Version: 3.7+ |
− | * Google Eclipse Plugin [GECLIPSE] (If you want your portlet to use GWT) [ | + | * '''Google Eclipse Plugin''' [GECLIPSE] (If you want your portlet to use GWT) [https://developers.google.com/eclipse/docs/getting_started GECLIPSE] |
− | ** GWT 2.7.0 (Bundled with the above Plugin) | + | ** GWT 2.7.0 SDK (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] | ||
+ | === 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): | ||
+ | |||
+ | [[File:Project_creation_mojo.png|500px]] | ||
+ | |||
+ | Click next and enter the maven coordinates for your artifact, see below for an example: | ||
+ | |||
+ | [[File:Entry_point_maven_coordinate_definitions.png|500px]]<br> | ||
+ | |||
+ | '''Sample''' is going to be the entry point class of your gwt application. <br> | ||
+ | |||
+ | Hit finish and the gwt-maven-plugin will create a sample gwt project for you. If you use Indigo at a certain point it will fail creating the goal i18n and the goal generateAsync, this should not happen with Eclipse starting from Juno. Don't worry, if it happens just follow these steps: | ||
+ | |||
+ | * open your pom.xml, locate the GWT Maven Plugin <plugin> node and delete the goals generating errors (generateAsync, i18n) see picture below: | ||
+ | [[File:Goals.png]] | ||
+ | |||
+ | * Open the GreetingService interface and generate its related ServiceAsync: | ||
+ | [[File:Async.png]] | ||
+ | |||
+ | * Open your entrypoint class and make it compile by: | ||
+ | ** deleting this line: private final Messages messages = GWT.create(Messages.class); | ||
+ | ** inserting explicit stings in the sendButton constructor and setText method as shown below: | ||
+ | [[File:Emoty.png]] | ||
===Writing a GWT application=== | ===Writing a GWT application=== | ||
− | You can find useful information about how to write a gwt application on the | + | 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> |
− | <br> | + | |
====<span style="color:red;">! IMPORTANT INFO</span>==== | ====<span style="color:red;">! IMPORTANT INFO</span>==== | ||
In the entrypoint-class, in the onModuleLoad() method: | In the entrypoint-class, in the onModuleLoad() method: | ||
When you are about to add your widget in the main page,<br> | When you are about to add your widget in the main page,<br> | ||
instead of writing: | instead of writing: | ||
− | <source lang=" | + | <source lang="java">RootPanel.get().add(<your widget>); |
</source> | </source> | ||
you must write: | you must write: | ||
− | <source lang=" | + | <source lang="java">RootPanel.get(<a unique id for the DIV>).add(<your widget>); |
</source> | </source> | ||
− | So, the generated html will be placed in the predefined ''div'' instead of being placed in | + | 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 | ||
+ | |||
+ | <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> | ||
+ | |||
+ | Fix the web.xml file as specified above and retry, now your browser should start and you can try your first GWT mavenized application. | ||
+ | |||
+ | [[File:First_application.png]] | ||
=== Adapting your Portlet to Liferay Portal === | === Adapting your Portlet to Liferay Portal === | ||
gCube as chosen to move to Liferay Portal from its 1.9 release. | 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 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: | |
− | + | <source lang="java"> | |
− | + | public class MyGCubePortletNamePortlet extends GenericPortlet { | |
+ | public void doView(RenderRequest request, RenderResponse response)throws PortletException, IOException { | ||
+ | response.setContentType("text/html"); | ||
+ | ScopeHelper.setContext(request); | ||
+ | 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 1 empty property file into your project war WEB-INF folder and name it | + | To access the GenericPortlet and the ScopeHelper classes add the following dependencies to your pom.xml |
+ | <source lang="xml"> | ||
+ | <dependency> | ||
+ | <groupId>javax.portlet</groupId> | ||
+ | <artifactId>portlet-api</artifactId> | ||
+ | <scope>provided</scope> | ||
+ | </dependency> | ||
+ | <dependency> | ||
+ | <groupId>org.gcube.portal</groupId> | ||
+ | <artifactId>custom-portal-handler</artifactId> | ||
+ | <scope>provided</scope> | ||
+ | </dependency> | ||
+ | </source> | ||
+ | |||
+ | 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 ==== | ==== Content for the above files ==== | ||
− | <MyGCubePortletName> | + | |
+ | * <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 6.0.0//EN" | ||
+ | "http://www.liferay.com/dtd/liferay-portlet-app_6_0_0.dtd"> | ||
<liferay-portlet-app> | <liferay-portlet-app> | ||
<portlet> | <portlet> | ||
− | <portlet-name> | + | <portlet-name>MyGCubePortletName</portlet-name> |
− | <layout-cacheable>false</layout-cacheable | + | <layout-cacheable>false</layout-cacheable> |
− | <instanceable>false</instanceable | + | <instanceable>false</instanceable> |
− | <ajaxable>false</ajaxable | + | <ajaxable>false</ajaxable> |
− | + | <!-- LOCATION CSS HERE --> | |
− | + | <header-portlet-css>/MyGCubePortletName.css</header-portlet-css> | |
</portlet> | </portlet> | ||
<role-mapper> | <role-mapper> | ||
Line 65: | Line 181: | ||
</liferay-portlet-app> | </liferay-portlet-app> | ||
</source> | </source> | ||
− | |||
... | ... | ||
---- | ---- | ||
Line 74: | Line 189: | ||
<source lang="xml"> | <source lang="xml"> | ||
<?xml version="1.0"?> | <?xml version="1.0"?> | ||
− | <!DOCTYPE display PUBLIC "-//Liferay//DTD Display | + | <!DOCTYPE display PUBLIC "-//Liferay//DTD Display 6.0.0//EN" |
− | + | "http://www.liferay.com/dtd/liferay-display_6_0_0.dtd"> | |
− | + | <display> | |
− | + | <category name="gCube Applications"> | |
− | + | <portlet id="MyGCubePortletName" /> | |
− | + | </category> | |
+ | </display> | ||
</source> | </source> | ||
---- | ---- | ||
Line 86: | Line 202: | ||
<source lang="xml"> | <source lang="xml"> | ||
− | name= | + | name=MyGCubePortletName |
module-group-id=liferay | module-group-id=liferay | ||
module-incremental-version=1 | module-incremental-version=1 | ||
Line 93: | Line 209: | ||
change-log= | change-log= | ||
page-url=http://www.d4science.org | page-url=http://www.d4science.org | ||
− | author= | + | author= Author's name |
licenses=EUPL | licenses=EUPL | ||
− | </source> | + | </source> <br> |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
+ | * '''JSP file's content''' | ||
+ | In your GWT Project html file seek the following part: | ||
<source lang="xml"> | <source lang="xml"> | ||
<!-- --> | <!-- --> | ||
Line 122: | Line 224: | ||
</source> | </source> | ||
− | + | 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 | |
− | + | ||
<source lang="xml"> | <source lang="xml"> | ||
Line 129: | Line 230: | ||
--%> | --%> | ||
<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> | ||
Line 135: | Line 235: | ||
</source> | </source> | ||
− | + | If you performed the above procedure correctly now your project should look similar to the picture below: | |
− | + | [[File:Project_structure.png|500px]] | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | |||
=== Compile GWT Inside the Portlet Project === | === Compile GWT Inside the Portlet Project === | ||
'''You can now compile your GWT Application (Inside the portlet Project) by using the Eclipse Plugin''' as shown below | '''You can now compile your GWT Application (Inside the portlet Project) by using the Eclipse Plugin''' as shown below | ||
+ | [[File:Gcube_compile_project.png|800px]] | ||
+ | === 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]]<br> | |
− | + | ||
== 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 | + | 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). | 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=" | + | <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 username in the session and the scope depending on the context automatically | ||
Line 186: | Line 267: | ||
</source> | </source> | ||
− | Moreover, | + | 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=" | + | <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> | ||
− | |||
== Migrating from Liferay 6.0 to Liferay 6.2.5 == | == 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. | |
− | + | Specifically: | |
− | = | + | * DTD declaration into liferay-display.xml changes to |
+ | <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 changes to | |
+ | <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 changes to |
− | + | <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 handle users, organizations and roles that are objects available in Liferay. You are discouraged to use Liferay's API directly, because of different abstractions made upon such concepts by the gCube framework. | |
− | + | ||
− | + | ||
− | <source lang= | + | Add this dependency to your pom.xml |
− | + | <source lang="xml"> | |
+ | <dependency> | ||
+ | <groupId>org.gcube.dvos</groupId> | ||
+ | <artifactId>usermanagement-core</artifactId> | ||
+ | <version>[2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT)</version> | ||
+ | <scope>provided</scope> | ||
+ | </dependency> | ||
+ | </source> | ||
+ | |||
+ | 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 but as Sites; | ||
+ | * GCubeUser, GCubeGroup, GCubeRole 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 site. A role, instead, is available in any site. | ||
+ | |||
+ | === PortalManager 2.0 === | ||
+ | The gCube Portal Manager is a component all portlets need to use to get the context where they are running. | ||
+ | A new instance of the manager can be retrieved by invoking | ||
+ | |||
+ | <source lang="java"> | ||
+ | PortalContext instance = PortalContext.getConfiguration(); | ||
+ | </source> | ||
+ | |||
+ | A part this information, the new version allows to retrieve: | ||
+ | |||
+ | * The name of the gateway and its URL | ||
+ | |||
+ | <source lang="java"> | ||
+ | // get the gateway name given the HttpServletRequest | ||
+ | instance.getGatewayName(request); | ||
+ | |||
+ | // get the gateway url given the HttpServletRequest | ||
+ | instance.getGatewayUrl(request); | ||
+ | </source> | ||
+ | |||
+ | * mail sender for the current site | ||
+ | <source lang="java"> | ||
+ | // get the current site email's sender (used when notifications are sent, for example), given the HttpServletRequest | ||
+ | instance.getSenderEmail(request); | ||
+ | </source> | ||
+ | |||
+ | * the name of the current infrastructure in which the client is running | ||
+ | |||
+ | <source lang="java"> | ||
+ | // get the current infrastructure name | ||
+ | instance.getInfrastructureName(); | ||
</source> | </source> | ||
=== ASL Social 1.0 === | === 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 : | ||
+ | * HttpServletRequest request; // the http request reaching the servlet | ||
+ | * String scope; // the current scope | ||
+ | * SocialNetworkingUser currUser. // the current user | ||
== Troubleshooting == | == Troubleshooting == | ||
Line 233: | Line 378: | ||
</pre> | </pre> | ||
− | ''' Cause | + | ''' Cause ''' : Tomcat classpath is listed before your GWT classpath |
− | ''' Solution | + | ''' 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 change 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 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. |
Revision as of 17:01, 2 May 2016
Contents
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):
Click next and enter the maven coordinates for your artifact, see below for an example:
Sample is going to be the entry point class of your gwt application.
Hit finish and the gwt-maven-plugin will create a sample gwt project for you. If you use Indigo at a certain point it will fail creating the goal i18n and the goal generateAsync, this should not happen with Eclipse starting from Juno. Don't worry, if it happens just follow these steps:
- open your pom.xml, locate the GWT Maven Plugin <plugin> node and delete the goals generating errors (generateAsync, i18n) see picture below:
- Open the GreetingService interface and generate its related ServiceAsync:
- Open your entrypoint class and make it compile by:
- deleting this line: private final Messages messages = GWT.create(Messages.class);
- inserting explicit stings in the sendButton constructor and setText method as shown below:
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.
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"); ScopeHelper.setContext(request); 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 and the ScopeHelper classes add the following dependencies to your pom.xml
<dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.gcube.portal</groupId> <artifactId>custom-portal-handler</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.0.0//EN" "http://www.liferay.com/dtd/liferay-portlet-app_6_0_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.0.0//EN" "http://www.liferay.com/dtd/liferay-display_6_0_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:
Compile GWT Inside the Portlet Project
You can now compile your GWT Application (Inside the portlet Project) by using the Eclipse Plugin as shown below
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
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 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 username in the session and the scope depending on the context 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();
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. Specifically:
- DTD declaration into liferay-display.xml changes to
<!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 changes to
<!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 changes to
<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 handle users, organizations and roles that are objects available in Liferay. You are discouraged to use Liferay's API directly, because of different abstractions made upon such concepts by the gCube framework.
Add this dependency to your pom.xml
<dependency> <groupId>org.gcube.dvos</groupId> <artifactId>usermanagement-core</artifactId> <version>[2.0.0-SNAPSHOT, 3.0.0-SNAPSHOT)</version> <scope>provided</scope> </dependency>
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 but as Sites;
- GCubeUser, GCubeGroup, GCubeRole 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 site. A role, instead, is available in any site.
PortalManager 2.0
The gCube Portal Manager is a component all portlets need to use to get the context where they are running. A new instance of the manager can be retrieved by invoking
PortalContext instance = PortalContext.getConfiguration();
A part this information, the new version allows to retrieve:
- The name of the gateway and its URL
// get the gateway name given the HttpServletRequest instance.getGatewayName(request); // get the gateway url given the HttpServletRequest instance.getGatewayUrl(request);
- mail sender for the current site
// get the current site email's sender (used when notifications are sent, for example), given the HttpServletRequest instance.getSenderEmail(request);
- the name of the current infrastructure in which the client is running
// get the current infrastructure name instance.getInfrastructureName();
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 :
- HttpServletRequest request; // the http request reaching the servlet
- String scope; // the current scope
- SocialNetworkingUser currUser. // the current user
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 change 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.