You are looking at documentation for an older release. Not what you want? See the current release documentation.
Spring MVC portlet is officially supported as of eXo Platform 4.2.
This tutorial shows you how to write a basic Spring portlet. Please visit chapter Portlet, Spring documentation for your further reading. Besides, you can download all source code used in this tutorial here.
If you are already familiar with Spring portlet and just want to know how to deploy it in eXo Platform, skip this section and go to Portlet deployment section.
Create a Maven project as follows:
Edit pom.xml
:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.exoplatform.samples</groupId>
<artifactId>sample-spring-mvc-portlet</artifactId>
<version>4.2.x</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>javax.portlet</groupId>
<artifactId>portlet-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc-portlet</artifactId>
<version>4.0.4.RELEASE</version>
<!-- <version>2.5.5</version> -->
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<finalName>spring-mvc-portlet</finalName>
</build>
</project>
Though the Spring version you see here is 4.0.4.RELEASE, it should work in older versions too. This example was tested against Spring 2.5.5 and Spring 4.0.4.RELEASE.
Edit Contact.java
. This class is the data model.
package org.exoplatform.samples.spring;
public class Contact {
private String firstName;
private String lastName;
private String displayName;
private String email;
public Contact(String firstName, String lastName, String displayName, String email) {
this.firstName = firstName;
this.lastName = lastName;
this.displayName = displayName;
this.email = email;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Edit ContactService.java
. This interface has only one method to get a list of contacts:
package org.exoplatform.samples.spring;
import java.util.Set;
public interface ContactService {
public Set getContacts();
}
Edit ContactServiceImpl.java
.
This class implements ContactService and provides a method to create some data for testing.
For simplicity, the data is in-memory.
package org.exoplatform.samples.spring;
import java.util.Set;
import java.util.LinkedHashSet;
import org.exoplatform.samples.spring.Contact;
public class ContactServiceImpl implements ContactService {
private static Set contactList = new LinkedHashSet();
public Set getContacts() {
if (contactList.size() == 0) {
initContacts();
}
return contactList;
}
public void initContacts() {
contactList.add(new Contact("John", "Smith", "John Smith", "john.smith@exo.com"));
contactList.add(new Contact("Mary", "Williams", "Mary Williams", "mary.williams@exo.com"));
contactList.add(new Contact("Jack", "Miller", "Jack Miller", "jack.miller@exo.com"));
contactList.add(new Contact("James", "Davis", "James Davis", "james.davis@exo.com"));
}
}
Edit ContactController.java
.
package org.exoplatform.samples.spring;
import org.springframework.web.portlet.mvc.AbstractController;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import org.springframework.web.portlet.ModelAndView;
import java.util.Set;
public class ContactController extends AbstractController {
private ContactService contactService;
public void setContactService(ContactService contactService) {
this.contactService = contactService;
}
@Override
public ModelAndView handleRenderRequestInternal(RenderRequest request, RenderResponse response) {
Set contacts = contactService.getContacts();
ModelAndView modelAndView = new ModelAndView("contactsView", "contacts", contacts);
return modelAndView;
}
}
Here you extend Spring's AbstractController
and override the method
handleRenderRequestInternal
.
This tutorial is limited in render phase. The super class has also the method
handleActionRequestInternal
that will be called in action phase.
Edit portlet.xml
.
<portlet-app 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 http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
version="2.0">
<portlet>
<portlet-name>contact</portlet-name>
<display-name>Contact</display-name>
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<portlet-info>
<title>Contact</title>
</portlet-info>
</portlet>
</portlet-app>
All Spring portlets have portlet-class DispatcherPortlet
that dispatches requests to controllers.
Each instance of DispatcherPortlet has its own WebApplicationContext that inherits all the beans already defined in the Root WebApplicationContext.
Each one also has its portlet-scope beans which are created during its initialization. Those beans are defined in
a file named {portlet-name}-portlet.xml
(that is, contact-portlet.xml
in next step).
Edit contact-portlet.xml
.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="contactController" class="org.exoplatform.samples.spring.ContactController">
<property name="contactService" ref="contactService" />
</bean>
<bean id="portletModeHandlerMapping" class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
<property name="portletModeMap">
<map>
<entry key="view" value-ref="contactController" />
</map>
</property>
</bean>
</beans>
Here you define some portlet-scoped beans: a controller and a handler mapping. The portlet-scoped bean definition overrides any bean with the same name defined at global scope.
The class ContactController
you wrote is declared as a bean and is responsible for handling the view mode.
Such beans as view resolver or services should be defined at the application context, so you do not have to define them for each portlet.
Edit web.xml
.
<web-app version="2.5" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name>spring-mvc-portlet</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>ViewRendererServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ViewRendererServlet</servlet-name>
<url-pattern>/WEB-INF/servlet/view</url-pattern>
</servlet-mapping>
</web-app>
The ViewRendererServlet
brings all the view rendering capabilities
that exist in the Spring servlet framework to the portlet.
Here you add a parameter, contextConfigLocation
, to customize the initialization of
DispatcherPortlet. The goal is to define some beans at the application scope.
Pay attention to the attribute version="2.5". The version 2.5 or greater is required. You should specify the version, otherwise it might not work in JBoss package.
Edit applicationContext.xml
.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="contactService" class="org.exoplatform.samples.spring.ContactServiceImpl" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
Here you define the service bean that will be consumed by the controller, and a view resolver.
The JstlView
is configured to resolve /WEB-INF/jsp/*.jsp
files.
Edit contactsView.jsp
.
<%@taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
<table border = "1">
<tr>
<th style="text-align:left">Name</th>
<th style="text-align:left">Email</th>
</tr>
<c:forEach items = "${contacts}" var ="contact">
<tr>
<td>${contact.displayName}</td>
<td>${contact.email}</td>
</tr>
</c:forEach>
</table>
Now, you can deploy the portlet in eXo Platform and test:
Here is a more intensive example that uses a lot of Spring annotations (so less xml configuration) and has several action methods.