This article is based on Liferay in Action, to be published 24-March-2011. It is being reproduced here by permission from Manning Publications. Manning publishes MEAP (Manning Early Access Program,) ebooks and pbooks. MEAPs are sold exclusively through Manning.com. All print book purchases include an ebook free of charge. When mobile formats become available all customers will be contacted and upgraded. Visit Manning.com for more information.
Portlets are easyPortlets are how all functionality is implemented in Liferay Portal, as well as in any other Java portal. Liferay is an ideal platform for serving and aggregating applications of all kinds, from collaborative message boards and wikis to sales force automation tools, customer relationship management tools and other business applications, to social applications such as photo sharing and games. The choices are limited only by your ingenuity and imagination, which I believe are practically limitless because I’ve seen the Liferay community doing all of the above and more with Liferay.
So how do you get started writing portlets? We’ll introduce the concept of writing portlets with a very simple example. This should get you started writing portlets according to the Java Portlet Standard.
If you check individual projects into a source code repository, you’ll want to check them out into a fully configured Plugins SDK, because the ant scripts in the projects depend on ant scripts within the Plugins SDK for their functionality. Let’s see how we would create new projects.
Creating a portlet plugin: Hello World
Creating portlets with the Plugins SDK is easy. Your portlet projects reside in the Plugins SDK folder in a folder called portlets. To create a new portlet, first decide what its name is going to be. You need both a project name (without spaces) and a display name (which can have spaces). When you’ve decided on your portlet’s name, you’re ready to create the project. On LUM, from the portlets folder, enter the following command:
./create.sh <project name> "<portlet title>"
So as a first exercise, let’s create the classic example, which, of course, is Hello World. To create a portlet with a project folder of hello-world and a portlet title of Hello World on LUM, type:
./create.sh hello-world "Hello World"
On Windows, you would type:
create.bat hello-world "Hello World"
You should get a BUILD SUCCESSFUL message from Ant, and there will now be a new folder inside the portlets folder in your Plugins SDK. This folder is your new portlet project. This is where you’ll be implementing your own functionality. At this point, if you wish, you can check in this project or your whole Plugins SDK to a source code repository to share your project with others. And, if you’re the command-line-plus-text-editor type of developer, you can get to work right away.
Alternatively, you can open your newly created portlet project in your IDE of choice and work with it there. If you do this, make sure that the project references some .jar files from your Liferay installation or you may get compile errors. Since the ant scripts in the Plugins SDK do this for you automatically, you don’t get these errors when working with the Plugins SDK.
Regardless of which tool you’ve used to add functionality to the plugin, after you’ve implemented some functionality, you’ll want to deploy your plugin to test it.
Deploying the Hello World plugin
You could deploy this portlet to Liferay right now if you wanted to because it’s already a Hello World portlet (how easy was that?). To deploy the portlet, go into its directory at the command prompt and type the following command (on all operating systems):
ant deploy
The portlet will be compiled and deployed to your running Liferay server. (You do have Liferay Portal running, right?) If you’re watching the logs, you’ll see status messages which look like this:
04:07:41,558 INFO [AutoDeployDir:176] Processing hello-world-portlet-6.0.0.1.war 04:07:41,560 INFO [PortletAutoDeployListener:81] Copying portlets for /home/me/code/bundles/deploy/hello-world-portlet-6.0.0.1.war 04:07:41,635 INFO [PluginPackageUtil:1347] Checking for available updates Expanding: /home/me/code/bundles/deploy/hello-world-portlet-6.0.0.1.war into /home/me/code/bundles/tomcat-6.0.18/temp/20100128040741564 Copying 1 file to /home/me/code/bundles/tomcat-6.0.18/temp/20100128040741564/WEB-INF Copying 1 file to /home/me/code/bundles/tomcat-6.0.18/temp/20100128040741564/WEB-INF/classes Copying 1 file to /home/me/code/bundles/tomcat-6.0.18/temp/20100128040741564/WEB-INF/classes Copying 1 file to /home/me/code/bundles/tomcat-6.0.18/temp/20100128040741564/WEB-INF Copying 1 file to /home/me/code/bundles/tomcat-6.0.18/temp/20100128040741564/META-INF Copying 24 files to /home/me/code/bundles/tomcat-6.0.18/webapps/hello-world-portlet Copying 1 file to /home/me/code/bundles/tomcat-6.0.18/webapps/hello-world-portlet Deleting directory /home/me/code/bundles/tomcat-6.0.18/temp/20100128040741564 04:07:43,263 INFO [PortletAutoDeployListener:91] Portlets for /home/me/code/bundles/deploy/hello-world-portlet-6.0.0.1.war copied successfully. Deployment will start in a few seconds. 04:07:44,827 INFO [PortletHotDeployListener:250] Registering portlets for hello-world-portlet 04:07:46,729 INFO [PluginPackageUtil:1391] Finished checking for available updates in 5092 ms 04:07:48,839 INFO [PortletHotDeployListener:376] 1 portlet for hello-world-portlet is available for use
When you see the available for use message, your plugin is deployed and can be used immediately. This applies to all plugin types, except for the Ext plugin.
To add the plugin to a page, log in to Liferay Portal using the administrative credentials (user name: test@liferay.com; password: test). From the Dockbar, select Add > More. Your generated portlet project by default is placed in the Samples category (we’ll see how to change this later). Open this category and you should see your portlet displayed similar to figure 1.
By default, portlets are generated as instanceable portlets, so you’ll see your portlet appear with the green icon. Drag it out onto the page. It should look like figure 2.
Obviously, it’s very easy to create a Hello World portlet: we didn’t even have to write any code. We’ll, of course, move on to a more functional portlet shortly but, for now, congratulations! You created your first portlet in less than five minutes.
We could go through all of the other types of plugins and create projects for them, but the process is exactly the same: go into the folder for the plugin type and use the Create script to create a new project. Once you’ve worked on the project, you deploy it in exactly the same manner—using the ant deploy command.
Making Hello World into Hello You
Open the Hello World project that you’ve just created. We’re going to turn this into the Hello You portlet.
Configuring Hello You
The first thing you should take a look at is the portlet.xml file, which you’ll find in the WEB-INF folder. This is the configuration file for the portlet. You’ll find a line in there that looks like this:
<portlet-class>com.liferay.util.bridges.mvc.MVCPortlet</portlet-class>
This defines what Java class implements the portlet. By default, projects are generated to use Liferay’s MVCPortlet, which we’ll get to later. It has certain benefits to the experienced developer, but it also abstracts much of the portlet lifecycle from you, which we don’t want to do just yet. So we’re going to implement our own portlet class the way the portlet specification recommends so that you can get familiar with the Portlet API and then move past it to the MVCPortlet and to other frameworks if you wish.
Since we aren’t going to use MVCPortlet, we have to change this line to point to the portlet class we’re going to create. So, modify this line so that it looks like this:
<portlet-class>com.liferayinaction.portlet.HelloYouPortlet</portlet-class>
Next, we need to tell the portal that our portlet implements edit mode as well as view mode (it assumes view mode; otherwise there would be no point to your portlet). So change the <supports> tag in your portlet.xml so it reads like this:
<supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> <portlet-mode>edit</portlet-mode> </supports>
You may have noticed in portlet.xml another tag called <init-param/>. This tag, as you’ve probably figured out, defines initialization parameters that can be used in your portlet. The default project defines a parameter called view-jsp, which defines the location of the JSP file that will be used to display the portlet in the view mode. This parameter can thus be used in the portlet class to forward processing over to the view.jsp file in the project. This worked in the initial portlet because this functionality was implemented already in MVCPortlet; now that we’re using our own portlet class, we’ll have to implement it ourselves.
Below the definition of your portlet class, add the additional initialization parameter below the existing one like this:
<init-param> <name>edit-jsp</name> <value>/edit.jsp</value> </init-param>
Imagine Darth Vader’s voice saying “All of our configuration is now complete.” We can now start implementing the logic of the portlet.
Create a package in your src folder called com.liferayinaction.portlet. In this package, create a Java class called HelloYouPortlet.java. This class will extend the GenericPortlet class, which is included with the Portlet API and is available in every portal implementation. So far, your class should look like the following code.
package com.liferayinaction.portlet; import javax.portlet.GenericPortlet; public class HelloYouPortlet extends GenericPortlet { }
Portlet initialization and implementing view mode
The first thing we want to do is implement the Portlet API’s init() method to pull the values from our initialization parameters into our portlet class. All we need to do is define two instance variables to hold these values and then implement the method:
protected String editJSP; protected String viewJSP; public void init() throws PortletException { editJSP = getInitParameter("edit-jsp"); viewJSP = getInitParameter("view-jsp"); }
Easy, right? We get access to the getInitParameter() method because we’re extending an existing class in the Portlet API. So, anything that is in those initialization parameters gets pulled into these variables.
Now we can actually implement the default view of our portlet. This is done by implementing a method called doView(). As you can imagine, there are similar methods for the other portlet modes, and we’ll also be implementing doEdit(). The functionality for this mode is simple: we want to retrieve a preference that may or may not have been stored for the logged-in user. If we don’t find one, we’ll simply print “Hello!” If we do find one, we’ll print a message that takes that preference and displays it in the message. So, if the user’s name is Mortimer Snerd, we’ll print “Hello Mortimer Snerd!”
Listing 1 The default view of your portlet
public void doView(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { PortletPreferences prefs = renderRequest.getPreferences(); String username = (String)prefs.getValue("name", "no"); if (username.equalsIgnoreCase("no")) { username=""; } req.setAttribute("userName", username); include(viewJSP, renderRequest, renderResponse); }
In this code, we first grab the PortletPreferences and check to see if the preference is there. The method for getting the preferences wants to know what the preference you’re looking for is called as well as a value to return if it can’t find the preference. So we provide name for the preference and no for the value to return if the preference isn’t found.
If we find a name, we store it as an attribute in the request. If not, we simply store an empty string. We then forward to the view.jsp file that was previously defined in our portlet.xml file.
Note that we’re using some objects here that are very similar to HttpServletRequest and HttpServletResponse, both of which you’re likely already familiar with. These are very similar to their servlet counterparts in that you can set parameters and attributes, retrieve information about the portlet’s environment, and more. In our case, we’re retrieving an object called PortletPreferences from the portlet instance.
We’re also calling a convenience method called include(), which looks like listing 2.
Listing 2 Shortening code with an include() convenience method
protected void include( String path, RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { PortletRequestDispatcher portletRequestDispatcher = getPortletContext().getRequestDispatcher(path); if (portletRequestDispatcher == null) { _log.error(path + " is not a valid include"); } else { portletRequestDispatcher.include( renderRequest, renderResponse); } }
Just like you can in a servlet-based web application, you can forward request processing to a JSP from a portlet. And just like in a servlet-based web application, you have to chain a whole bunch of methods together in order to accomplish it. To make our code neater, we can simply create a convenience method called include(), which chains all those methods together for us so that all we have to do is call this one method (which has a nice, short name) to forward processing to a JSP.
We’ve included a check here to make sure that the PortletRequestDispatcher we get out of the PortletContext is not null. If I’s null, we log that. To use this method, therefore, we’ll also have to enable logging in our portlet. Liferay ships with Apache’s Commons Logging classes, which make it easy to add log entries from our portlets. We only need to add an instance variable to the class for our _log object:
private static Log _log = LogFactory.getLog(HelloYouPortlet.class);
To complete the view mode of this portlet, we now have to implement the JSP to which we’re forwarding, which you can see in its entirety in listing 3.
Listing 3 Implementing the JSP for view mode
<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <jsp:useBean id="userName" class="java.lang.String" scope="request"/> #1 <portlet:defineObjects /> <p>This is the Hello You portlet.</p> <p>Hello <%=userName %>!</p> #1 Gets the bean out of the request
<portlet:defineObjects />
<p>This is the Hello You portlet.</p>
<p>Hello <%=userName %>!</p>
#1 Gets the bean out of the request
In the first line, we declare the portlet tag library. This again is a part of the Portlet API and is a standard across all portals. The next line should look familiar: we pull a bean out of the request that we had made available in the doView() method of our portlet (#1). There’s no difference in how this is done versus doing it with a servlet. The tag in the third line comes from the portlet tag library, which we declared in the first line. The defineObjects tag registers several objects with the JSP that may be useful: renderRequest, renderResponse, and portletConfig.
About backward compatibility The tag library declaration above is the declaration for Portlet 1.0. This has been done for backward compatibility, as we’re not using any features in this first portlet that require Portlet 2.0. This portlet, therefore, can be deployed on any Portlet 1.0 container (such as Liferay 4.4.x) or any Portlet 2.0 container (such as Liferay 5.0 and above).
The Portlet API’s RenderRequest and RenderResponse objects correspond to the HttpServletRequest and HttpServletResponse objects with which you may be familiar, and we’ve already used these in our portlet class. The PortletConfig object corresponds roughly with the ServletConfig object you would use in a Servlet, in that it holds data about the portlet and its environment. Because you may need to use these objects in your JSP’s display logic, these three variables are defined through the defineObjects tag, and it’s a good idea to always put this tag at the top of your JSPs.
Otherwise, the logic of this JSP is very simple. We pull the bean we stored in the request and print a “Hello” message. If the bean has a value, that value will be printed after the message; if not, nothing will be printed.
At this point, view mode is complete. You can actually deploy the portlet as it is and it will display a hello message. Since we haven’t implemented edit mode yet, though, it won’t have any functionality, so that’s what we’ll do next.
Implementing edit mode
Before we jump into the code for the edit mode, I need to tell you something about URLs in portlet applications. Most web developers are used to manipulating the URL directly. That’s not something you can do in a portlet application. In most cases, portlet URLs are linked with Portlet actions, which cause the portlet to do some processing. Portlet actions are defined using a special portlet URL object called an ActionURL. Because a portlet is a fragment of a page that is assembled at runtime by the portlet container, developers cannot simply define their own URLs like they can in regular web applications. Instead, they must be created programmatically. This is a bit of a paradigm shift, but it’s fairly easy to make the transition. It’s important to know that the contents of URLs must be generated by the portal server at runtime. Why? So that there are no conflicts between URLs generated by different portlets.
For example, consider a search portlet that sits on a page. This portlet can search for any uploaded content. Say, another portlet is placed on that page by a user. This other portlet contains a list of customers and has a search box at the top.
If developers knew ahead of time that these portlets would be placed on the same page, they could make sure that the URLs to the two separate search functions were different. But they didn’t know ahead of time that a portal administrator would do this, and so both search URLs point to /search in the application’s context. Can you see how there would be a conflict? Which application’s search would get called?
For this reason, the Portlet API provides URL objects. Developers can create URL objects programmatically and even set parameters in them. This allows the portlet container to generate the actual URL strings at runtime, preventing any conflicts like the one mentioned above. Since our implementation of edit mode consists of a very simple form that users will fill out and submit, we need a URL for the submit button. For this reason, we’ll have to create one using the API. So our doEdit() method in listing 4 reflects this.
Listing 4 Implementing doEdit() in our portlet
public void doEdit(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { renderResponse.setContentType("text/html"); PortletURL addName = renderResponse.createActionURL(); addName.setParameter("addName", "addName"); renderRequest.setAttribute("addNameUrl", addName.toString()); include(editJSP, renderRequest, renderResponse); }
When you add the code above, you’ll also have to add the import javax.portlet.PortletURL.
More about backward compatibility
The first line of code in listing 4, which sets the content type, is only required in the 1.0 (JSR-168) version of the API. It doesn’t hurt anything, so we have left it in the method above to maintain backward compatibility but, if you’re using Portlet 2.0 (JSR-286), it’s not necessary.
We’ve created an ActionURL and added a parameter called addName to it. We then put the URL in the request object so that it may be used in the JSP to which we’ll be forwarding the processing.
Let’s implement the JSP for the edit mode next. Create a file in docroot called edit.jsp. Remember that we earlier pointed to this file using an initialization parameter in portlet.xml. Listing 5 shows the edit.jsp file.
Listing 5 The JSP for edit mode
<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <jsp:useBean class="java.lang.String" id="addNameUrl" scope="request" /> <portlet:defineObjects /> <form id = "<portlet:namespace />helloForm" action="<%=addNameUrl %>" method="post"> <table> <tr> <td>Name:</td> <td><input type="text" name="username"></td> </tr> </table> <input type="submit" id="nameButton" title="Add Name" value="Add Name"> </form>
In listing 5, we first declare the tag library for the Portlet API as we did before and we then make our URL available to the JSP using the useBean tag to retrieve it from the request object and place it in a variable called addNameUrl.
After this is a simple HTML form. Notice that we use a portlet tag called namespace in front of our form name. This is done for similar reasons that portlet URLs exist: you could have multiple forms with the same name on the page and then they would conflict with each other. Using this tag, you allow the portal server to prepend a unique string to the front of your form’s name, thereby ensuring that no other portlet will contain a form with the same name as yours.
Note also that the form’s action is set to the value of the action name parameter in the ActionURL we created in the portlet class and then retrieved as a bean in the JSP. This URL has the parameter addName in it, which will be captured by our processAction method when this form is submitted so that processing can be directed to the right place.
Speaking of processAction, that is now the only missing element in our portlet. This method is part of the standard Portlet API. The processAction method is there to provide a place where portlet actions can be processed. A default implementation is provided with the API, but we’re going to override it for this portlet and provide our own implementation.
Portlet URLs can be of two types: a RenderURL or an ActionURL. A RenderURL simply tells the portlet to render itself again. Whatever parameters have been defined for the portlet at that time take effect and the portlet redraws itself. An ActionURL immediately forwards processing to the processAction method, where logic can be in place to determine which action has been taken by the user and then the appropriate processing can occur. We’ll implement our own version of this so that we can see how it works and then, later on, we’ll take advantage of other (better) implementations. Since we have only one action, the logic will be pretty simple, as you can see in listing 6.
Listing 6 Processing a portlet action
String addName = actionRequest.getParameter("addName"); if (addName != null) { PortletPreferences prefs = actionRequest.getPreferences(); prefs.setValue("name", actionRequest.getParameter("username")); prefs.store(); actionResponse.setPortletMode(PortletMode.VIEW); }
You’ll also need to add the following two imports to the top of your class:
import javax.portlet.PortletPreferences; import javax.portlet.PortletMode;
This code searches for a parameter in the portlet’s ActionRequest object called addName. We created this parameter earlier as part of an ActionURL in our doEdit() method. Simply by having the form’s action point to this URL, processing is directed to the four lines of code in the if statement. We could just as easily have called a different method instead, which is what you would normally do in a larger portlet so that you can keep only action processing logic here.
In any case, the parameter—called username—will be found in the request because it was a field on the form. The Portlet API is then accessed in order to store a preference for this particular user. The preference is called name and we use whatever value was in the parameter. Yes, I know: we’re not doing any field validation. Normally you would do that before storing anything, but I wanted to keep this example as simple as possible. This key/value pair will now be stored for that portlet/user combination.
The last thing this code does is set the portlet mode back to view mode. When that is done, doView() will be called and the user will now get the “Hello <name>” message.
To summarize, we now have all of the processing for the portlet’s edit mode in place. The doEdit() method creates a URL with an action name parameter of addName. Processing is then forwarded to the edit.jsp file where this parameter is used as the action of a form. The form contains one field, which the user will use to type his or her name. When the form is submitted, the portlet’s processAction method will run and retrieve the action’s name parameter, which will have the value addName. Checking for this value leads us to code that stores the name the user submitted as a PortletPreference. To keep the example simple, we have not implemented any validation on the data submitted.
Rather than look at it piecemeal as we did above, it’s sometimes easier to see how things work by showing the whole example. So listing 7 shows what the entire portlet should look like.
Listing 7 The complete Hello You portlet
package com.liferayinaction.portlet; import java.io.IOException; import javax.portlet.ActionRequest; import javax.portlet.ActionResponse; import javax.portlet.GenericPortlet; import javax.portlet.PortletException; import javax.portlet.PortletMode; import javax.portlet.PortletPreferences; import javax.portlet.PortletRequestDispatcher; import javax.portlet.PortletURL; import javax.portlet.RenderRequest; import javax.portlet.RenderResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class HelloYouPortlet extends GenericPortlet { public void init() throws PortletException { editJSP = getInitParameter("edit-jsp"); viewJSP = getInitParameter("view-jsp"); } public void doEdit(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { renderResponse.setContentType("text/html"); PortletURL addName = renderResponse.createActionURL(); addName.setParameter("addName", "addName"); renderRequest.setAttribute("addNameUrl", addName.toString()); include(editJSP, renderRequest, renderResponse); } public void doView(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { PortletPreferences prefs = renderRequest.getPreferences(); String username = (String) prefs.getValue("name", "no"); if (username.equalsIgnoreCase("no")) { username = ""; } renderRequest.setAttribute("userName", username); include(viewJSP, renderRequest, renderResponse); } public void processAction( ActionRequest actionRequest, ActionResponse actionResponse) throws IOException, PortletException { String addName = actionRequest.getParameter("addName"); if (addName != null) { PortletPreferences prefs = actionRequest.getPreferences(); prefs.setValue( "name", actionRequest.getParameter("username")); prefs.store(); actionResponse.setPortletMode(PortletMode.VIEW); } } protected void include( String path, RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException { PortletRequestDispatcher portletRequestDispatcher = getPortletContext().getRequestDispatcher(path); if (portletRequestDispatcher == null) { _log.error(path + " is not a valid include"); } else { portletRequestDispatcher.include( renderRequest, renderResponse); } } protected String editJSP; protected String viewJSP; private static Log _log = LogFactory.getLog(HelloYouPortlet.class); }
Deploying and testing your portlet
Because we used the Plugins SDK to create this project, it came with its own Ant script, which makes it very easy to build and deploy the project. If your IDE supports Ant, you can deploy the project right from within your IDE. If you’re not using an IDE, you can run a simple command to deploy your project.
First, start your Liferay server. Again, if you’re using an IDE and have followed the instructions from the previous chapter, you can do this right from within your IDE. Once Liferay has started, keep a window open to Liferay’s console so you can watch the deploy take place.
Next, run the deploy task from the Ant build script. To do this from the command line (on any operating system), issue the following command from the project folder:
ant deploy
Once you receive the BUILD SUCCESSFUL message, turn your attention to the Liferay console. You should see your portlet deploy right away.
Your new portlet is now indistinguishable from any other portlet deployed in Liferay. Go to http://localhost:8080 and log in using the administrative credentials:
User Name: test@liferay.com
Password: test
From the Dockbar and click Add > More. You’ll see many categories of portlets displayed. Open the one labeled Sample. You’ll see the Hello World portlet listed there. Drag it off the list and drop it in the right-most column, if it’s not there already. If you followed along with the previous chapter, the portlet should already be there.
You’ll now see your portlet displayed. You can close the Add > More window by clicking the red X in its top right corner.
The default message “Hello!” is being displayed in the portlet. This is the way it’s supposed to function: we haven’t set our Portlet Preference yet, so the portlet does’nt know our name. To get to Edit Mode, click the button in the title bar of the portlet that has a wrench on it, and then click the Preferences link. (Liferay Portal displays a portlet’s edit mode as a Preferences menu item.) You’ll be brought to the portlet’s edit mode and your edit.jsp will be displayed.
Type your name and click the Add Name button. Your Portlet Preference will be stored, and because we changed the mode of the portlet back to View in our processAction method, the portlet will redisplay itself in view mode. Because our Portlet Preference is now set, the portlet will display the name we entered.
Congratulations! You have just written your first portlet!
Changing the portlet’s name and category
Notice that, when you added the portlet, you had to select it from the Sample category in the Add > More window. You also had to add the Hello World portlet, but now our portlet is called Hello You. The portlet is in a suboptimal category because the generated portlet project defaults to this category, and the portlet has the wrong name because we created Hello World first. If you’d like to create your own category for your portlet, this is easy to do. At the same time, we also want to rename the portlet so that it’s no longer called Hello World. We can do these things by editing three XML files:
- portlet.xml
- liferay-portlet.xml
- liferay-display.xml
Renaming the portlet
First, let’s change the name of the portlet. Open portlet.xml, which is in your WEB-INF folder. There are two places in this file where we need to change the name. The first takes effect at a system level, and the second takes effect for the portlet title.
Find the two lines in the file that look like this:
<portlet-name>hello-world</portlet-name> <display-name>Hello World</display-name>
Change them so that they read like this:
<portlet-name>hello-you</portlet-name> <display-name>Hello You</display-name>
Next, find the the <portlet-info> section of the file:
<portlet-info> <title>Hello World</title> <short-title>Hello World</short-title> <keywords>Hello World</keywords> </portlet-info>
Change it so that it reads like this:
<portlet-info> <title>Hello You</title> <short-title>Hello You</short-title> <keywords>Hello You</keywords> </portlet-info>
Next, open liferay-portlet.xml, which is in the same folder. Find the following line, which is in the <portlet> tag:
<portlet-name>hello-world</portlet-name>
Change it so it reads:
<portlet-name>hello-you</portlet-name>
Creating a custom category
Finally, in the same WEB-INF folder, you’ll find a file called liferay-display.xml. This file controls the category under which your portlet appears in the Add > More window. Open this file and you’ll see the code shown in the following listing.
Listing 8 Changing the category for your portlet
<?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="category.sample"> <portlet id="hello-you" /> </category> </display>
Change the category name to something else, like My Portlets. The code would then read:
<display> <category name="My Portlets"> <portlet id="hello-you" /> </category> </display>
Now go ahead and deploy your portlet again. Refresh the page. You’ll see that an interesting thing has happened to your portlet, shown in figure 3.
Your portlet no longer exists. You just renamed your portlet to Hello You, but Liferay thinks a Hello World portlet is on the page because, well, you put it there. Of course, now Liferay can’t find it, so it displays this message. So, what you’ll have to do is remove the portlet from the page and add your newly renamed portlet to the page instead. To remove the portlet, just click the x.
Next, open the Add > More window. You’ll see your new category displayed, and your portlet is now displayed there, as in figure 4.
You can now drag the portlet over to the page. When you do, notice what it says now (figure 5).
The preference you stored earlier when the portlet was named Hello World was saved for that portlet. Now that you have renamed it, that portlet no longer exists. For all intents and purposes, even though the code is exactly the same, this is now a new portlet according to Liferay, so Liferay treats it as such. Its title bar also now properly displays “Hello You,” because you configured it that way in the portlet.xml file.
Summary
You’ve just created a portlet according to the Java standard. This portlet really doesn’t do very much, but it serves as a good introduction to several highly used features of the portlet API. We’ve used only Portlet 1.0 features in this portlet, so you could deploy it on containers that support either Portlet 1.0 (like Liferay 4.4.x and below) or Portlet 2.0 (like Liferay 5.0.x and above). The skills you’ve learned here will provide a good foundation for creating more complex portlet projects.