Integration of a GWT Rich Client Tool to the Sakai's platform
We want to talk about some of the concerns that we had about the integration of a GWT Rich Client tool to the Sakai's platform and how they have been addressed.
Exploring Differents Solutions
Firstly, we needed to prove the feasibility of the integration of a tool based on GWT with the Sakai's platform.
Hopefully, Sakai is based for the most part on a services architecture using the Spring Framework and the integration of GWT to Spring has already been done by others George Georgovassilis .
An important issue was : How to reach Spring services from the RPC servlet?
Anyway, Spring is well known for its integration capabilities, but we have to find the best way.
Three possibilities were explored :
- Centralized approach:
Using the ApplicationContext with one main Backing Bean able to reach other Spring services
- Not centralized approach:
We directly call each Bean Identifier of the Spring service we need (as necessary) from the RPC Servlet.
- Spring service mapping:
We also looked at the Open Source GWTSpringController library of George Georgovassilis.
It was working fine, but we found blocking situation with the actual Sakai. The library did not use the same Spring version (2.5). So using different class loader we were unable to share the same Spring context with Sakai.
Centralized approach finally adopted
After experimental work, the Centralized approach involving a main Backing Bean and the Spring applicationContext was finally adopted.
With the help of people from Sakai Québec (CRIM), we did the integration of GWT and Sakai. What we did is pretty similar to the suggestion of Luis Filipe Lobo in the Sakai dev Forum, using servlet extension of GWT servlet, Sakai listener and tool registering into Sakai.
Diagram of the integration
Let us consider, in order to fix the ideas, a figure of the centralized approach.
You can see the Main Backing Bean from which we access to other Spring services is allowed.
To successfully register this Main Backing Bean, you have to define an applicationContext.xml file and include a ContextLoaderListener into your Webapp configuration file web.xml
The roles of the Java Servlets
Java Servlets running on the Sakai's server are used for:
- RPC communication with the GWT client (GWT RPC Servlet)
Each GWT Servlet is a standard Java Servlet that extends the GWT RemoteServiceServlet class
The OsylEditorGwtServiceImpl.java file resides into the folder server (i.e. /osyl-tool/tool/src/java/org/sakaiquebec/opensyllabus/server/) of the opensyllabus-tool project.
... import com.google.gwt.user.server.rpc.RemoteServiceServlet; import org.sakaiquebec.opensyllabus.client.rpc.OsylEditorGwtService; ... import org.sakaiquebec.opensyllabus.server.OsylBackingBean; import org.springframework.web.context.WebApplicationContext; ... import org.sakaiproject.tool.api.Tool; ... public class OsylEditorGwtServiceImpl extends RemoteServiceServlet implements OsylEditorGwtService { ... private OsylBackingBean osylServices; private ServletContext servletContext = null; private WebApplicationContext webAppContext = null; ... public OsylEditorGwtServiceImpl() { ; } // must have ... public void init() { servletContext = getServletContext(); webAppContext = WebApplicationContextUtils.getWebApplicationContext(servletContext); // Spring bean injection if (webAppContext != null) { osylServices = (OsylBackingBean) webAppContext.getBean("backingBean"); } ... }
- Indirect Spring services access
- Security management
- Extra request information access
Spring Application Context File
So, we have to define a Spring Application Context using the applicationContext.xml file which resides into the folder WEB-INF (i.e. /osyl-tool/tool/src/webapp/WEB-INF/) of the opensyllabus-tool project.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <!-- This file generated by Sakai App Builder -AZ --> <beans> <!-- list the backing beans here --> <bean id="backingBean" class="org.sakaiquebec.opensyllabus.server.OsylBackingBean" init-method="init"> <property name="osylResourceService" ref="org.sakaiquebec.opensyllabus.api.OsylResourceService" /> <property name="osylSecurityService" ref="org.sakaiquebec.opensyllabus.api.OsylSecurityService" /> <property name="osylConfigService" ref="org.sakaiquebec.opensyllabus.api.OsylConfigService" /> </bean> </beans>
Sakai's tool registration & configuration
- Tool registration is done by providing the tool xml file
In order to define a tool in Sakai you have to register it by providing an XML definition of your tool.
That is, sakai.opensyllabus.tool.xml file into the folder tools (i.e. /osyl-tool/tool/src/webapp/tools/) of the opensyllabus-tool project.
<?xml version="1.0"?> <registration> <tool id="sakai.opensyllabus.tool" title="OpenSyllabus" description="Sakai OpenSyllabus Tool"> <category name="course" /> <category name="project" /> <keyword name="GWT,syllabus" /> </tool> </registration>
- Tool configuration is done using the web.xml file
The Web application configuration file web.xml resides into the folder WEB-INF (i.e. /osyl-tool/tool/src/webapp/WEB-INF/) of the opensyllabus-tool project.
In addition, we have to include a ToolListener and a ContextLoaderListener into the web.xml
...
<listener>
<listener-class>org.sakaiproject.util.ToolListener</listener-class>
</listener>
<listener>
<listener-class>org.sakaiproject.util.ContextLoaderListener</listener-class>
</listener>
...
Thereafter, there are also other information related to configuration (Sakai filters, servlet definitions, etc) contained into the web.xml configuration file.
Here is a full web.xml sample file :
<?xml version="1.0" encoding="UTF-8"?> <web-app id="WebApp_9" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <display-name>sakai-opensyllabus-tool</display-name> <description>sakai-opensyllabus-tool</description> <filter> <filter-name>sakai.request</filter-name> <filter-class>org.sakaiproject.util.RequestFilter</filter-class> <init-param> <param-name>tool.placement</param-name> <param-value>false</param-value> </init-param> </filter> <filter-mapping> <filter-name>sakai.request</filter-name> <servlet-name>sakai.opensyllabus.tool</servlet-name> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> <dispatcher>INCLUDE</dispatcher> </filter-mapping> <servlet> <servlet-name>sakai.opensyllabus.tool</servlet-name> <servlet-class>uk.ac.cam.caret.sakai.WebappToolServlet</servlet-class> <load-on-startup>2</load-on-startup> </servlet> <servlet> <servlet-name>sakai.opensyllabus.editor</servlet-name> <servlet-class>org.sakaiquebec.opensyllabus.server.OsylEditorGwtServiceImpl</servlet-class> </servlet> <servlet-mapping> <servlet-name>sakai.opensyllabus.editor</servlet-name> <url-pattern>/OsylGwtService</url-pattern> </servlet-mapping> <listener> <listener-class>org.sakaiproject.util.ToolListener</listener-class> </listener> <listener> <listener-class>org.sakaiproject.util.ContextLoaderListener</listener-class> </listener> </web-app>
The roles of the JSP entry page
Now, we will see what are the roles of the JSP entry page (index.jsp) which resides in the folder webapp (i.e. /osyl-tool/tool/src/webapp/) of the opensyllabus-tool project.
- First it gives access to the GWT compiled JavaScript.
Here is a sample of the script tag you have to put into your index.jsp page to give access to the GWT compiled JS.
<!-- This script loads our GWT compiled module. -->
<!-- Any GWT meta tags must be added before this line. -->
<script language='javascript'
src='<%=request.getContextPath() %>/org.sakaiquebec.opensyllabus.OsylEditorEntryPoint
/org.sakaiquebec.opensyllabus.OsylEditorEntryPoint.nocache.js'>
</script>
- With JSP scriptlet (directly coded in the index.jsp) you can use specific logic to choose between different compiled GWT applications
- Provides the CSS Link
<link rel="stylesheet" type="text/css" href="osylcoconfigs/default/skin/osylcore.css" />
- Initializes some META to set the language (I18N)?
For instance, you can initialize the locale to use for the GWT interface.
In the sample code below, we show an access to the Sakai user preference language (via the ResourceLoader) and the setting of the locale GWT property.
... <%@ page import="org.sakaiproject.util.ResourceLoader"%> ... <% ResourceLoader rb = new ResourceLoader(); Locale sessionLocale = rb.getLocale(); String locale = sessionLocale.toString(); ... %> <html> <head> <meta name="gwt:property" content="locale=<%=locale%>"> ...
- Controls the tool display size
With the index.jsp page, you can also control the tool display size by using some special Sakai provided attributes. (Please, search in the Sakai's documentation to get more information about the "sakai.html.body.onload" attribute).
...
<html>
<head>
...
<!-- Headers from Sakai -->
<%= request.getAttribute("sakai.html.head") %>
<script> // Size of the JS application
function myLoad() {
setTimeout("<%=request.getAttribute("sakai.html.body.onload") %>", 500);
}
</script>
</head>
<body onload="myLoad()">
</body>
</html>