You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 12 Next »

Struts 2 Portlet Tutorial - Creating a simple Bookmark Portlet

Work in progress

Using version 2.1.1-SNAPSHOT of the portlet plugin

Note that this tutorial assumes that you're familiar with basic Struts 2 web application programming.

If you have not used Struts 2 before, please check out some of the other Struts 2 tutorials first.

Preparations

In this tutorial we will use eclipse as our IDE. If you do not have Eclipse, you can download it from http://www.eclipse.org.

The project itself will be set up using Maven 2. Maven 2 is available from http://maven.apache.org.

If you have not used the maven-eclipse-plugin before, you need to set up the Eclipse workspace with a variable that points to the Maven 2 repository. To do this, type

mvn -Declipse.workspace=<path-to-eclipse-workspace> eclipse:add-maven-repo

Creating the project

We'll use Maven 2 with the Struts 2 Portlet Archetype to create a skeleton project for our portlet application. From the command line, issue the command:

mvn archetype:create -DarchetypeGroupId=org.apache.struts -DarchetypeArtifactId=struts2-archetype-portlet -DarchetypeVersion=2.1.1-SNAPSHOT -DartifactId=bookmark-portlet -DgroupId=com.mycompany

This will set up the maven 2 structure for us and also set up the basic configuration needed to create a Struts 2 portlet. The archetype creates a sample HelloWorld portlet that shows off some of the basic principles of Struts 2 portlet programming. To test the set up, type

mvn jetty:run -P pluto-embedded

in a command prompt. Open a browser and point your browser to http://localhost:8080/bookmark-portlet/pluto/index.jsp and play around.

Looking at the basics

To see how the basic HelloWorld example works, let's look at some of the configuration files, starting with the JSR168 portlet descriptor

src/main/webapp/WEB-INF/portlet.xml
<?xml version="1.0" encoding="UTF-8"?>

<portlet-app
    version="1.0"
    xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
    id="bookmark-portlet">

	<portlet id="HelloPortlet">
		<description xml:lang="EN">Simple hello world portlet</description>
		<portlet-name>HelloPortlet</portlet-name>
		<display-name xml:lang="EN">bookmark-portlet</display-name>
		
		<portlet-class>org.apache.struts2.portlet.dispatcher.Jsr168Dispatcher</portlet-class>
		
		<!-- The namespace for the actions configured for view mode -->
		<init-param>
			<name>viewNamespace</name>
			<value>/view</value>
		</init-param>
		
		<!-- The default action to invoke in view mode. -->
		<init-param>
			<name>defaultViewAction</name>
			<value>index</value>
		</init-param>
		
		<!-- The namespace for the actions configured for edit mode -->
		<init-param>
			<name>editNamespace</name>
			<value>/edit</value>
		</init-param>
		
		<!-- The default action to invoke in edit mode. -->
		<init-param>
			<name>defaultEditAction</name>
			<value>index!input</value>
		</init-param>
		
		<expiration-cache>0</expiration-cache>
		
		<supports>
			<mime-type>text/html</mime-type>
			<portlet-mode>view</portlet-mode>
			<portlet-mode>edit</portlet-mode>
		</supports>
		
		<supported-locale>en</supported-locale>
		
		<portlet-info>
			<title>HelloPortlet</title>
			<short-title>HelloPortlet</short-title>
			<keywords>struts 2,portlet,hello,world</keywords>
		</portlet-info>
	</portlet>
  
</portlet-app>

The important parts to notice are the portlet-class and init-param elements. The portlet-class elements is always org.apache.struts2.portlet.dispatcher.Jsr168Dispatcher (or a subclass, if you have added some custom functionality). This is the portlet that acts as the dispatcher for the Struts 2 framework, and translates incoming user interaction to action requests that Struts 2 understands. The init-params viewNamespace, defaultViewAction, editNamespace and defaultEditAction set up some defaults for the dispatcher when the portlet encounters a portlet mode without a specific action. Here, we set up the view portlet mode to map to the /view action namespace, and the edit portlet mode to map to the /edit action namespace. We also specify that the default actions for the mentioned portlet modes are index and index!input respectively. We will recognize these namespaces in the next file:

struts.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
   <package name="default" extends="struts-portlet-default" namespace="/view">
        <action name="index" class="com.mycompany.HelloAction">
	    <result>/WEB-INF/jsp/view/index.jsp</result>
	</action>
   </package>
	
   <package name="edit" extends="struts-portlet-default" namespace="/edit">
	<action name="index" class="com.mycompany.UpdateNameAction">
	    <result type="redirectAction">
	        <param name="actionName">index</param>
		<param name="portletMode">view</param>
	    </result>
	    <result name="input">/WEB-INF/jsp/edit/index.jsp</result>
	</action>
    </package>
</struts>

As we can see, the actions for the view portlet mode is in the default package, with /view as namespace, and the actions for the edit portlet mode is in the edit package, with /edit as namespace.

Import the project into Eclipse

Now let's import the project into Eclipse. First, type

mvn eclipse:eclipse -P pluto-embedded

Then start Eclipse (if you have not already done so), and import the project using "File -> Import -> General -> Existing Projects into Workspace". Browse to the folder where you created the project and press finish. Your portlet project should now be setup up with all dependencies in place.

Creating the Bookmark domain object

To represent the bookmarks, we'll create a simple domain object. We'll keep it really simple, so the Bookmark object will only have a name and a url property. In the src/main/java folder, create Bookmark.java:

Bookmark.java
package com.mycompany.domain;

public class Bookmark {
   private String name;
   private String url;

   public Bookmark(String name, String url) {
      this.name = name;
      this.url = url;
   }
	
   public String getName() {
      return name;
   }

   public String getUrl() {
      return url;
   }
}

Adding bookmarks

Since we'll use the PortletPreferences to store our bookmarks, adding a bookmark can only happen in the edit portlet mode. So we'll create a simple action for this purpose, and configure it in the edit configuration package. In normal Struts 2 fashion, we'll create an action object with the properties we need:

AddBookmark.java
package com.mycompany;

public class AddBookmarkAction extends DefaultActionSupport {

   private String name;
   private String url;

   public void setName(String name) {
      this.name = name;
   }

   public void setUrl(String url) {
      this.url = url;
   }

   @Override
   public String execute() throws Exception {
      return SUCCESS;
   }
}

And in struts.xml, remove the existing configuration for the edit package and add an entry for the action:

struts.xml
<package name="edit" extends="struts-portlet-default" namespace="/edit">
	
   <action name="index" class="com.mycompany.AddBookmarkAction">
      <result name="input">/WEB-INF/jsp/edit/index.jsp</result>
   </action>

</package>

Let's create the input form so we have something to display. The form is really simple, with a label and a text field for each of the properties in the Bookmark domain object. In the src/main/webapp/WEB-INF/jsp/edit folder, create an index.jsp file:

index.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>

<h2>Manage bookmarks</h2>

<s:form action="index">
   <table>
      <s:textfield name="name" label="Name"/>
      <s:textfield name="url" label="URL"/>
      <s:submit value="Add"/>
   </table>
</s:form>

The textfields maps to the property names we have defined in AddBookmarkAction. Before we continue, let's check that everything is configured correctly and check that our portlet can be run. In a command prompt, change into the directory where you have created the project and issue the command mvn jetty:run -P pluto-embedded. Then open http://localhost:8080/bookmark-portlet/pluto/index.jsp and click on the edit portlet window control. If everything is set up correctly, you should see a form like this:

INSERT IMAGE

If you try to submit data in the form, it will obviously not work since we have not implemented any logic to add bookmarks yet. That will be our next task. Since we'll need a PortletPreferences reference, we'll have the action implement the PortletPreferencesAware interface that will instruct Struts 2 to inject this into our action, without the need for us to look it up manually. When we have the refefence to the PortletPreferences object, we'll implement logic to store the bookmark (or rather the bookmark's properties, since we can only store Strings in the preferences object):

AddBookmarkAction.java
package com.mycompany;

public class AddBookmarkAction extends DefaultActionSupport implements PortletPreferencesAware {

   private String name;
   private String url;
	
   private PortletPreferences portletPreferences;

   public void setName(String name) {
      this.name = name;
   }

   public void setUrl(String url) {
      this.url = url;
   }
	
   public void setPortletPreferences(PortletPreferences portletPreferences) {
      this.portletPreferences = portletPreferences;	
   }

   @Override
   public String execute() throws Exception {
      portletPreferences.setValue(name, url);
      portletPreferences.store();
      return SUCCESS;
   }
}

After the bookmark has been stored, we'll just redirect back to the input form:

struts.xml
<package name="edit" extends="struts-portlet-default" namespace="/edit">
	
   <action name="index" class="com.mycompany.AddBookmarkAction">
      <result type="redirectAction">
         <param name="actionName">index!input</param>
      </result>
      <result name="input">/WEB-INF/jsp/edit/index.jsp</result>
   </action>

</package>

Now we can add some bookmarks. We don't get much feedback though, so let's proceed...

Listing the bookmarks

The bookmarks will be listed in the view portlet mode, so we'll create a ListBookmarksAction and configure it in the default package:

src/main/java/com/mycompany/ListBookmarksAction.java
package com.mycompany;

public class ListBookmarksAction extends DefaultActionSupport implements PortletPreferencesAware {
   private List<Bookmark> bookmarks = new ArrayList<Bookmark>();
   private PortletPreferences portletPreferences;

   public List<Bookmark> getBookmarks() {
      return bookmarks;
   }

   public void setPortletPreferences(PortletPreferences portletPreferences) {
      this.portletPreferences = portletPreferences;
   }

   @Override
   public String execute() throws Exception {
      // For simplicity, we'll assume that only bookmarks are stored in the preferences.
      Map<String, String[]> preferencesMap = portletPreferences.getMap();
      for(Map.Entry<String, String[]> entry : preferencesMap.entrySet()) {
         bookmarks.add(new Bookmark(entry.getKey(), entry.getValue()[0]));
      }
      return SUCCESS;
   }
}

Again we use the PortletPreferencesAware to get the PortletPreferences injected in our action. Then we just get all the values from the preferences and add them as a Bookmark instance in an ArrayList.

Obviously, we'll need a jsp to view the list of bookmarks:

src/main/webapp/WEB-INF/jsp/view/index.jsp
<%@ taglib prefix="s" uri="/struts-tags" %>

<strong>Bookmarks</strong>
<p>
   <table>
   <s:iterator value="%{bookmarks}" var="bookmark">
      <tr>
         <td><a href="<s:property value="%{url}"/>" target="_blank"><s:property value="%{name}"/></a></td>
      </tr>
   </s:iterator>
   </table>
</p>

In the JSP, we just iterate over the list of Bookmarks and print the properties in the iterator loop.

In struts.xml, remove the default package, and add this instead:

src/main/resources/struts.xml
<package name="view" extends="struts-portlet-default" namespace="/view">
   <action name="index" class="com.mycompany.ListBookmarksAction">
      <result>/WEB-INF/jsp/view/index.jsp</result>
   </action>
</package>

When you're ready, go back to a command prompt and start the server again (mvn jetty:run -P pluto-embedded), open a browser and start adding some bookmarks. When you go back to view mode after adding a few, you'll see the bookmarks listed:

  • No labels