📜 ⬆️ ⬇️

Portlet Basics

Hi habrareyudi! Today I want to tell you about one interesting technology that I just met recently - this is portlet technology. Although on Habré there are already a couple of mentions about portlets, but I didn’t find anything intelligible there. Therefore, I decided to write my own article where I want to show in practice how to program portlets. In this case, simultaneously inserting some theoretical information. And taking into account the fact that there is very little documentation in Russian, then I would like to tell about it twice :)

To be in the know, we need a few definitions:

The portal, in its simplest consideration, is a software platform containing a portlet-container, which in turn supports the Portlet API and allows us to run portlets. Also, the portal usually provides the means for managing groups and users, as well as wide possibilities for their customization.

A portlet is a separate small web application that runs on a portal, the portal in turn aggregates one or more portlets on a separate web page, which are usually configured for individual users and groups of the portal. As a result, we get a regular web page filled with several web applications. And yet, portlets have a lot of servlet analogies, I think that many who are well aware of this will notice. Once again to draw analogies and I will not dwell on this.
')
In this article I will describe the development of portlets within the JSR 168 specification, but I will not talk about choosing a portal, installing it, deploying portlets, etc. I’ll say right away that I was developing an application on IBM Web Sphere Portal 6 . If someone tells about the features of development and deployment on other portals like Jboss Portal or some other, I would be very grateful :)

So let's start developing, and we will develop ... in fact, for a long time I thought it would be better to cite as an example, I wanted the example not to be something trivial like Hello World, I wanted the portlet to find practical application and my choice fell on the portlet for aggregation RSS . Why? Actually!

So, our mini TK:
It is necessary to write a portlet that will collect rss from various channels and display it. At the same time, we will provide each portal user with the opportunity to customize the portlet to fit their needs, i.e. create and delete displayed channels, as well as set the number of news from each channel and the frequency of their updates.

First, let's create a list of classes, libraries and jsp-nis that we need for our application:

1) HabraRssPortlet - the actual portlet class itself.
2) RssUtil - class for working with RSS.
3) rssutils - a library (of course, you can write the parser itself, but it will take time and probably won't work out right away - it’s proven by experience)
3) HabraRssPortletView.jsp - for displaying Rss.
4) HabraRssPortletEdit.jsp - to configure the portlet by the user.
5) HabraRssPortletHelp.jsp - where there will be something like “About”
6) portlet.xml and web.xml - well, I think it is clear for what ...

Let's go!

I want to start with the deployment descriptor portlet.xml in order to explain some of the points that we will need later, the web.xml descriptor will not lead here, everything is painfully simple. If someone is interested, then you can always look at the attachment.

portlet.xml
<? xml version ="1.0" encoding ="UTF-8" ? > <br> < portlet-app xmlns ="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version ="1.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" id ="ru.habrahabr.rssportlet.HabraRssPortlet.a97fbe5bd1" > <br> < portlet > <br> < portlet-name > HabraRssPortlet </ portlet-name > <br> < display-name > HabraRssPortlet </ display-name > <br> < display-name xml:lang ="ru" > HabraRssPortlet </ display-name > <br> < portlet-class > ru.habrahabr.rssportlet.HabraRssPortlet </ portlet-class > <br> < init-param > <br> < name > wps.markup </ name > <br> < value > html </ value > <br> </ init-param > <br> < expiration-cache > 0 </ expiration-cache > <br> < supports > <br> < mime-type > text/html </ mime-type > <br> < portlet-mode > view </ portlet-mode > <br> < portlet-mode > edit </ portlet-mode > <br> < portlet-mode > config </ portlet-mode > <br> < portlet-mode > help </ portlet-mode > <br> </ supports > <br> < supported-locale > ru </ supported-locale > <br> < resource-bundle > ru.habrahabr.rssportlet.nl.HabraRssPortletResource </ resource-bundle > <br> < portlet-info > <br> < title > HabraRssPortlet </ title > <br> < short-title > HabraRssPortlet </ short-title > <br> < keywords > HabraRssPortlet </ keywords > <br> </ portlet-info > <br> < portlet-preferences > <br> <br> < preference > <br> < name > updateFrequency </ name > <br> <!-- in minutes --> <br> < value > 10 </ value > <br> </ preference > <br> < preference > <br> < name > itemQuantity </ name > <br> < value > 3 </ value > <br> </ preference > <br> < preference > <br> < name > rssLinks </ name > <br> < value > www.vz.ru/rss.xml </ value > <br> </ preference > <br><br> </ portlet-preferences > <br> </ portlet > <br> < custom-portlet-mode > <br> < portlet-mode > config </ portlet-mode > <br> </ custom-portlet-mode > <br> </ portlet-app > <br> <br> * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="UTF-8" ? > <br> < portlet-app xmlns ="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version ="1.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" id ="ru.habrahabr.rssportlet.HabraRssPortlet.a97fbe5bd1" > <br> < portlet > <br> < portlet-name > HabraRssPortlet </ portlet-name > <br> < display-name > HabraRssPortlet </ display-name > <br> < display-name xml:lang ="ru" > HabraRssPortlet </ display-name > <br> < portlet-class > ru.habrahabr.rssportlet.HabraRssPortlet </ portlet-class > <br> < init-param > <br> < name > wps.markup </ name > <br> < value > html </ value > <br> </ init-param > <br> < expiration-cache > 0 </ expiration-cache > <br> < supports > <br> < mime-type > text/html </ mime-type > <br> < portlet-mode > view </ portlet-mode > <br> < portlet-mode > edit </ portlet-mode > <br> < portlet-mode > config </ portlet-mode > <br> < portlet-mode > help </ portlet-mode > <br> </ supports > <br> < supported-locale > ru </ supported-locale > <br> < resource-bundle > ru.habrahabr.rssportlet.nl.HabraRssPortletResource </ resource-bundle > <br> < portlet-info > <br> < title > HabraRssPortlet </ title > <br> < short-title > HabraRssPortlet </ short-title > <br> < keywords > HabraRssPortlet </ keywords > <br> </ portlet-info > <br> < portlet-preferences > <br> <br> < preference > <br> < name > updateFrequency </ name > <br> <!-- in minutes --> <br> < value > 10 </ value > <br> </ preference > <br> < preference > <br> < name > itemQuantity </ name > <br> < value > 3 </ value > <br> </ preference > <br> < preference > <br> < name > rssLinks </ name > <br> < value > www.vz.ru/rss.xml </ value > <br> </ preference > <br><br> </ portlet-preferences > <br> </ portlet > <br> < custom-portlet-mode > <br> < portlet-mode > config </ portlet-mode > <br> </ custom-portlet-mode > <br> </ portlet-app > <br> <br> * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="UTF-8" ? > <br> < portlet-app xmlns ="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" version ="1.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd" id ="ru.habrahabr.rssportlet.HabraRssPortlet.a97fbe5bd1" > <br> < portlet > <br> < portlet-name > HabraRssPortlet </ portlet-name > <br> < display-name > HabraRssPortlet </ display-name > <br> < display-name xml:lang ="ru" > HabraRssPortlet </ display-name > <br> < portlet-class > ru.habrahabr.rssportlet.HabraRssPortlet </ portlet-class > <br> < init-param > <br> < name > wps.markup </ name > <br> < value > html </ value > <br> </ init-param > <br> < expiration-cache > 0 </ expiration-cache > <br> < supports > <br> < mime-type > text/html </ mime-type > <br> < portlet-mode > view </ portlet-mode > <br> < portlet-mode > edit </ portlet-mode > <br> < portlet-mode > config </ portlet-mode > <br> < portlet-mode > help </ portlet-mode > <br> </ supports > <br> < supported-locale > ru </ supported-locale > <br> < resource-bundle > ru.habrahabr.rssportlet.nl.HabraRssPortletResource </ resource-bundle > <br> < portlet-info > <br> < title > HabraRssPortlet </ title > <br> < short-title > HabraRssPortlet </ short-title > <br> < keywords > HabraRssPortlet </ keywords > <br> </ portlet-info > <br> < portlet-preferences > <br> <br> < preference > <br> < name > updateFrequency </ name > <br> <!-- in minutes --> <br> < value > 10 </ value > <br> </ preference > <br> < preference > <br> < name > itemQuantity </ name > <br> < value > 3 </ value > <br> </ preference > <br> < preference > <br> < name > rssLinks </ name > <br> < value > www.vz.ru/rss.xml </ value > <br> </ preference > <br><br> </ portlet-preferences > <br> </ portlet > <br> < custom-portlet-mode > <br> < portlet-mode > config </ portlet-mode > <br> </ custom-portlet-mode > <br> </ portlet-app > <br> <br> * This source code was highlighted with Source Code Highlighter .



Below is the main class of our application - HabraRssPortlet , which is a descendant of the GenericPortlet class, and it, in turn, is an abstract class and provides the default implementation of the Portlet interface. The same GenericPortlet provides support for several modes: VIEW, EDIT, HELP , and sometimes custom modes such as CONFIG, but this already depends on the specific implementation of the portal. For the end user, all three modes are most often represented by various displayed information and available functionality in one mode or another. The transition between the modes is carried out either through the portlet menu, or through the buttons / links. Usually, for any portlet class, several methods are overloaded, most often these are doView, doEdit and doHelp methods , each of which is responsible for the corresponding mode.

HabraRssPortlet.java
package ru.habrahabr.rssportlet;

import java.io.*;
import java.util.*;
import javax.portlet.*;

public class HabraRssPortlet extends GenericPortlet {

public static final String JSP_FOLDER = "/_HabraRssPortlet/jsp/" ;
public static final String VIEW_JSP = "HabraRssPortletView" ;
public static final String EDIT_JSP = "HabraRssPortletEdit" ;
public static final String HELP_JSP = "HabraRssPortletHelp" ;
public static final String CONFIG_JSP = "HabraRssPortletConfig" ;
public static final String RSS_LINKS = "rssLinks" ;
public static final String UPDATE_FREQUENCY = "updateFrequency" ;
public static final String ITEM_QUANTITY = "itemQuantity" ;
public static final String NEW_RSS_LINK = "newRssLink" ;
public static final String ADD_NEW_RSS_LINK = "addNewRssLink" ;
public static final String DELETE_RSS_LINK = "deleteRssLink" ;
public static final String RSS_LINKS_LIST = "rssLinksList" ;
public static final String FREQUENCY = "frequency" ;
public static final String QUANTITY = "quantity" ;
public static final String SET_FREQUENCY = "setFrequency" ;
public static final String SET_QUANTITY = "setQuantity" ;
public static final String ERROR_MESSAGE = "errorMessage" ;
public static final String UPDATE_FEEDS = "updateFeeds" ;
public static final String EDIT_BACK = "editBack" ;

private static HashMap rssMap = new HashMap();
private static HashMap updateTimeMap = new HashMap();

public void doView(RenderRequest request, RenderResponse response)throws PortletException, IOException {
response.setContentType(request.getResponseContentType());
PortletPreferences preferences = request.getPreferences();

if (!rssMap.containsKey(request.getUserPrincipal().getName())){
rssMap.put(request.getUserPrincipal().getName(), new ArrayList ());
updateTimeMap.put(request.getUserPrincipal().getName(), new Long(0));

}

if (RssUtil.checkUpdateTime(getLastUpdateTime(request), preferences)) {
ArrayList rssItems = ( ArrayList )rssMap. get (request.getUserPrincipal().getName());
rssItems.clear();
rssItems = RssUtil.updateRssFeeds(preferences);
System. out .println( "News for user " + request.getUserPrincipal().getName() + " updated!" );
rssMap.put(request.getUserPrincipal().getName(), rssItems);
setLastUpdateTime(request);

}

int interval = Integer.parseInt(preferences.getValue(UPDATE_FREQUENCY, "60" ));
System. out .println( "Time befor update (in seconds) = " + ((interval * 60) - ((System.currentTimeMillis() - getLastUpdateTime(request).longValue()) / 1000 )));

request.setAttribute( "rssMap" , rssMap. get (request.getUserPrincipal().getName()));
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, VIEW_JSP));
rd.include(request, response);
}

public void doEdit(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType(request.getResponseContentType());
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(
getJspFilePath(request, EDIT_JSP));
rd.include(request, response);
}

protected void doHelp(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType(request.getResponseContentType());
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, HELP_JSP));
rd.include(request, response);
}

public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException {
PortletPreferences preferences = request.getPreferences();

if (request.getParameter(ADD_NEW_RSS_LINK) != null ) {
if (request.getParameter(NEW_RSS_LINK).trim().length() > 0) {
String [] links = ( String []) preferences.getValues(HabraRssPortlet.RSS_LINKS, null );
String newLink = request.getParameter(NEW_RSS_LINK);
preferences.setValues(RSS_LINKS, addToArray(links, newLink));
preferences.store();
System. out .println( ">>>>>>>>>>>>>>>> New rss link added " + newLink);
} else {
response.setRenderParameter(ERROR_MESSAGE, "Link must be not empty!" );
}
}

if (request.getParameter(DELETE_RSS_LINK) != null ) {
if (request.getParameter(RSS_LINKS_LIST) != null
&& request.getParameter(RSS_LINKS_LIST).trim().length() > 0 ) {
String [] links = ( String []) preferences.getValues(
HabraRssPortlet.RSS_LINKS, null );
String deletedLink = request.getParameter(RSS_LINKS_LIST);
if (isFound(links, deletedLink)) {
preferences.setValues(RSS_LINKS, deleteFromArray(links, deletedLink));
preferences.store();
System. out .println( ">>>>>>>>>>>>>>>> Rss link deleted " + deletedLink);
}
} else {
response.setRenderParameter(ERROR_MESSAGE, "Choose a link!" );
}
}

if (request.getParameter(SET_FREQUENCY) != null ) {
if (request.getParameter(FREQUENCY).trim().length() > 0
&& isDigit(request.getParameter(FREQUENCY))) {
String frequency = request.getParameter(FREQUENCY);
preferences.setValue(UPDATE_FREQUENCY, frequency);
preferences.store();
System. out .println( ">>>>>>>>>>>>>>>> Frequency updated " + frequency);
} else {
response.setRenderParameter(ERROR_MESSAGE, "Set valid frequency!" );
}
}

if (request.getParameter(SET_QUANTITY) != null ) {
if (request.getParameter(QUANTITY).trim().length() > 0
&& isDigit(request.getParameter(QUANTITY))) {
String quantity = request.getParameter(QUANTITY);
preferences.setValue(ITEM_QUANTITY, quantity);
preferences.store();
System. out .println( ">>>>>>>>>>>>>>>> Quantity updated " + quantity);
} else {
response.setRenderParameter(ERROR_MESSAGE, "Set valid quantity!" );
}
}

if (request.getParameter(UPDATE_FEEDS) != null ) {
ArrayList rssItems = ( ArrayList )rssMap. get (request.getUserPrincipal().getName());
rssItems.clear();
rssItems = RssUtil.updateRssFeeds(preferences);
System. out .println( "News fo user " + request.getUserPrincipal().getName() + " updated!" );
rssMap.put(request.getUserPrincipal().getName(), rssItems);
setLastUpdateTime(request);
}

if (request.getParameter(EDIT_BACK) != null ) {
response.setPortletMode(PortletMode.VIEW);
}



}

private static String getJspFilePath(RenderRequest request, String jspFile) {
String markup = request.getProperty( "wps.markup" );
if (markup == null )
markup = getMarkup(request.getResponseContentType());
return JSP_FOLDER + markup + "/" + jspFile + "."
+ getJspExtension(markup);
}

private static String getMarkup( String contentType) {
if ( "text/vnd.wap.wml" .equals(contentType))
return "wml" ;
else
return "html" ;
}

private static String getJspExtension( String markupName) {
return "jsp" ;
}

private static String [] addToArray( String [] array, String s) {
String [] ans = new String [array.length + 1];
System.arraycopy(array, 0, ans, 0, array.length);
ans[ans.length - 1] = s;
return ans;
}

private static String [] deleteFromArray( String [] array, String s) {
String [] ans = new String [array.length - 1];
int addIndex = 0;
for ( int i = 0; i < array.length; i++) {
if (array[i].equals(s)) {
addIndex++;
continue ;
}
ans[i - addIndex] = array[i];
}
return ans;
}

private static boolean isFound( String [] array, String s) {
boolean found = false ;
for ( int i = 0; i < array.length; i++) {
if (array[i].equals(s)) {
found = true ;
break ;
}
}
return found;
}

private static boolean isDigit( String s) {
char [] array = s.toCharArray();
for ( int i = 0; i < array.length; i++) {
if (!Character.isDigit(array[i])) {
return false ;
}
}
return true ;
}

private static Long getLastUpdateTime(PortletRequest request){
return (Long)updateTimeMap. get (request.getUserPrincipal().getName());
}

private static void setLastUpdateTime(PortletRequest request){
updateTimeMap.put(request.getUserPrincipal().getName(), new Long(System.currentTimeMillis()));
}


}

* This source code was highlighted with Source Code Highlighter .
package ru.habrahabr.rssportlet;

import java.io.*;
import java.util.*;
import javax.portlet.*;

public class HabraRssPortlet extends GenericPortlet {

public static final String JSP_FOLDER = "/_HabraRssPortlet/jsp/" ;
public static final String VIEW_JSP = "HabraRssPortletView" ;
public static final String EDIT_JSP = "HabraRssPortletEdit" ;
public static final String HELP_JSP = "HabraRssPortletHelp" ;
public static final String CONFIG_JSP = "HabraRssPortletConfig" ;
public static final String RSS_LINKS = "rssLinks" ;
public static final String UPDATE_FREQUENCY = "updateFrequency" ;
public static final String ITEM_QUANTITY = "itemQuantity" ;
public static final String NEW_RSS_LINK = "newRssLink" ;
public static final String ADD_NEW_RSS_LINK = "addNewRssLink" ;
public static final String DELETE_RSS_LINK = "deleteRssLink" ;
public static final String RSS_LINKS_LIST = "rssLinksList" ;
public static final String FREQUENCY = "frequency" ;
public static final String QUANTITY = "quantity" ;
public static final String SET_FREQUENCY = "setFrequency" ;
public static final String SET_QUANTITY = "setQuantity" ;
public static final String ERROR_MESSAGE = "errorMessage" ;
public static final String UPDATE_FEEDS = "updateFeeds" ;
public static final String EDIT_BACK = "editBack" ;

private static HashMap rssMap = new HashMap();
private static HashMap updateTimeMap = new HashMap();

public void doView(RenderRequest request, RenderResponse response)throws PortletException, IOException {
response.setContentType(request.getResponseContentType());
PortletPreferences preferences = request.getPreferences();

if (!rssMap.containsKey(request.getUserPrincipal().getName())){
rssMap.put(request.getUserPrincipal().getName(), new ArrayList ());
updateTimeMap.put(request.getUserPrincipal().getName(), new Long(0));

}

if (RssUtil.checkUpdateTime(getLastUpdateTime(request), preferences)) {
ArrayList rssItems = ( ArrayList )rssMap. get (request.getUserPrincipal().getName());
rssItems.clear();
rssItems = RssUtil.updateRssFeeds(preferences);
System. out .println( "News for user " + request.getUserPrincipal().getName() + " updated!" );
rssMap.put(request.getUserPrincipal().getName(), rssItems);
setLastUpdateTime(request);

}

int interval = Integer.parseInt(preferences.getValue(UPDATE_FREQUENCY, "60" ));
System. out .println( "Time befor update (in seconds) = " + ((interval * 60) - ((System.currentTimeMillis() - getLastUpdateTime(request).longValue()) / 1000 )));

request.setAttribute( "rssMap" , rssMap. get (request.getUserPrincipal().getName()));
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, VIEW_JSP));
rd.include(request, response);
}

public void doEdit(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType(request.getResponseContentType());
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(
getJspFilePath(request, EDIT_JSP));
rd.include(request, response);
}

protected void doHelp(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType(request.getResponseContentType());
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, HELP_JSP));
rd.include(request, response);
}

public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException {
PortletPreferences preferences = request.getPreferences();

if (request.getParameter(ADD_NEW_RSS_LINK) != null ) {
if (request.getParameter(NEW_RSS_LINK).trim().length() > 0) {
String [] links = ( String []) preferences.getValues(HabraRssPortlet.RSS_LINKS, null );
String newLink = request.getParameter(NEW_RSS_LINK);
preferences.setValues(RSS_LINKS, addToArray(links, newLink));
preferences.store();
System. out .println( ">>>>>>>>>>>>>>>> New rss link added " + newLink);
} else {
response.setRenderParameter(ERROR_MESSAGE, "Link must be not empty!" );
}
}

if (request.getParameter(DELETE_RSS_LINK) != null ) {
if (request.getParameter(RSS_LINKS_LIST) != null
&& request.getParameter(RSS_LINKS_LIST).trim().length() > 0 ) {
String [] links = ( String []) preferences.getValues(
HabraRssPortlet.RSS_LINKS, null );
String deletedLink = request.getParameter(RSS_LINKS_LIST);
if (isFound(links, deletedLink)) {
preferences.setValues(RSS_LINKS, deleteFromArray(links, deletedLink));
preferences.store();
System. out .println( ">>>>>>>>>>>>>>>> Rss link deleted " + deletedLink);
}
} else {
response.setRenderParameter(ERROR_MESSAGE, "Choose a link!" );
}
}

if (request.getParameter(SET_FREQUENCY) != null ) {
if (request.getParameter(FREQUENCY).trim().length() > 0
&& isDigit(request.getParameter(FREQUENCY))) {
String frequency = request.getParameter(FREQUENCY);
preferences.setValue(UPDATE_FREQUENCY, frequency);
preferences.store();
System. out .println( ">>>>>>>>>>>>>>>> Frequency updated " + frequency);
} else {
response.setRenderParameter(ERROR_MESSAGE, "Set valid frequency!" );
}
}

if (request.getParameter(SET_QUANTITY) != null ) {
if (request.getParameter(QUANTITY).trim().length() > 0
&& isDigit(request.getParameter(QUANTITY))) {
String quantity = request.getParameter(QUANTITY);
preferences.setValue(ITEM_QUANTITY, quantity);
preferences.store();
System. out .println( ">>>>>>>>>>>>>>>> Quantity updated " + quantity);
} else {
response.setRenderParameter(ERROR_MESSAGE, "Set valid quantity!" );
}
}

if (request.getParameter(UPDATE_FEEDS) != null ) {
ArrayList rssItems = ( ArrayList )rssMap. get (request.getUserPrincipal().getName());
rssItems.clear();
rssItems = RssUtil.updateRssFeeds(preferences);
System. out .println( "News fo user " + request.getUserPrincipal().getName() + " updated!" );
rssMap.put(request.getUserPrincipal().getName(), rssItems);
setLastUpdateTime(request);
}

if (request.getParameter(EDIT_BACK) != null ) {
response.setPortletMode(PortletMode.VIEW);
}



}

private static String getJspFilePath(RenderRequest request, String jspFile) {
String markup = request.getProperty( "wps.markup" );
if (markup == null )
markup = getMarkup(request.getResponseContentType());
return JSP_FOLDER + markup + "/" + jspFile + "."
+ getJspExtension(markup);
}

private static String getMarkup( String contentType) {
if ( "text/vnd.wap.wml" .equals(contentType))
return "wml" ;
else
return "html" ;
}

private static String getJspExtension( String markupName) {
return "jsp" ;
}

private static String [] addToArray( String [] array, String s) {
String [] ans = new String [array.length + 1];
System.arraycopy(array, 0, ans, 0, array.length);
ans[ans.length - 1] = s;
return ans;
}

private static String [] deleteFromArray( String [] array, String s) {
String [] ans = new String [array.length - 1];
int addIndex = 0;
for ( int i = 0; i < array.length; i++) {
if (array[i].equals(s)) {
addIndex++;
continue ;
}
ans[i - addIndex] = array[i];
}
return ans;
}

private static boolean isFound( String [] array, String s) {
boolean found = false ;
for ( int i = 0; i < array.length; i++) {
if (array[i].equals(s)) {
found = true ;
break ;
}
}
return found;
}

private static boolean isDigit( String s) {
char [] array = s.toCharArray();
for ( int i = 0; i < array.length; i++) {
if (!Character.isDigit(array[i])) {
return false ;
}
}
return true ;
}

private static Long getLastUpdateTime(PortletRequest request){
return (Long)updateTimeMap. get (request.getUserPrincipal().getName());
}

private static void setLastUpdateTime(PortletRequest request){
updateTimeMap.put(request.getUserPrincipal().getName(), new Long(System.currentTimeMillis()));
}


}

* This source code was highlighted with Source Code Highlighter .
package ru.habrahabr.rssportlet;

import java.io.*;
import java.util.*;
import javax.portlet.*;

public class HabraRssPortlet extends GenericPortlet {

public static final String JSP_FOLDER = "/_HabraRssPortlet/jsp/" ;
public static final String VIEW_JSP = "HabraRssPortletView" ;
public static final String EDIT_JSP = "HabraRssPortletEdit" ;
public static final String HELP_JSP = "HabraRssPortletHelp" ;
public static final String CONFIG_JSP = "HabraRssPortletConfig" ;
public static final String RSS_LINKS = "rssLinks" ;
public static final String UPDATE_FREQUENCY = "updateFrequency" ;
public static final String ITEM_QUANTITY = "itemQuantity" ;
public static final String NEW_RSS_LINK = "newRssLink" ;
public static final String ADD_NEW_RSS_LINK = "addNewRssLink" ;
public static final String DELETE_RSS_LINK = "deleteRssLink" ;
public static final String RSS_LINKS_LIST = "rssLinksList" ;
public static final String FREQUENCY = "frequency" ;
public static final String QUANTITY = "quantity" ;
public static final String SET_FREQUENCY = "setFrequency" ;
public static final String SET_QUANTITY = "setQuantity" ;
public static final String ERROR_MESSAGE = "errorMessage" ;
public static final String UPDATE_FEEDS = "updateFeeds" ;
public static final String EDIT_BACK = "editBack" ;

private static HashMap rssMap = new HashMap();
private static HashMap updateTimeMap = new HashMap();

public void doView(RenderRequest request, RenderResponse response)throws PortletException, IOException {
response.setContentType(request.getResponseContentType());
PortletPreferences preferences = request.getPreferences();

if (!rssMap.containsKey(request.getUserPrincipal().getName())){
rssMap.put(request.getUserPrincipal().getName(), new ArrayList ());
updateTimeMap.put(request.getUserPrincipal().getName(), new Long(0));

}

if (RssUtil.checkUpdateTime(getLastUpdateTime(request), preferences)) {
ArrayList rssItems = ( ArrayList )rssMap. get (request.getUserPrincipal().getName());
rssItems.clear();
rssItems = RssUtil.updateRssFeeds(preferences);
System. out .println( "News for user " + request.getUserPrincipal().getName() + " updated!" );
rssMap.put(request.getUserPrincipal().getName(), rssItems);
setLastUpdateTime(request);

}

int interval = Integer.parseInt(preferences.getValue(UPDATE_FREQUENCY, "60" ));
System. out .println( "Time befor update (in seconds) = " + ((interval * 60) - ((System.currentTimeMillis() - getLastUpdateTime(request).longValue()) / 1000 )));

request.setAttribute( "rssMap" , rssMap. get (request.getUserPrincipal().getName()));
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, VIEW_JSP));
rd.include(request, response);
}

public void doEdit(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType(request.getResponseContentType());
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(
getJspFilePath(request, EDIT_JSP));
rd.include(request, response);
}

protected void doHelp(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType(request.getResponseContentType());
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(getJspFilePath(request, HELP_JSP));
rd.include(request, response);
}

public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException {
PortletPreferences preferences = request.getPreferences();

if (request.getParameter(ADD_NEW_RSS_LINK) != null ) {
if (request.getParameter(NEW_RSS_LINK).trim().length() > 0) {
String [] links = ( String []) preferences.getValues(HabraRssPortlet.RSS_LINKS, null );
String newLink = request.getParameter(NEW_RSS_LINK);
preferences.setValues(RSS_LINKS, addToArray(links, newLink));
preferences.store();
System. out .println( ">>>>>>>>>>>>>>>> New rss link added " + newLink);
} else {
response.setRenderParameter(ERROR_MESSAGE, "Link must be not empty!" );
}
}

if (request.getParameter(DELETE_RSS_LINK) != null ) {
if (request.getParameter(RSS_LINKS_LIST) != null
&& request.getParameter(RSS_LINKS_LIST).trim().length() > 0 ) {
String [] links = ( String []) preferences.getValues(
HabraRssPortlet.RSS_LINKS, null );
String deletedLink = request.getParameter(RSS_LINKS_LIST);
if (isFound(links, deletedLink)) {
preferences.setValues(RSS_LINKS, deleteFromArray(links, deletedLink));
preferences.store();
System. out .println( ">>>>>>>>>>>>>>>> Rss link deleted " + deletedLink);
}
} else {
response.setRenderParameter(ERROR_MESSAGE, "Choose a link!" );
}
}

if (request.getParameter(SET_FREQUENCY) != null ) {
if (request.getParameter(FREQUENCY).trim().length() > 0
&& isDigit(request.getParameter(FREQUENCY))) {
String frequency = request.getParameter(FREQUENCY);
preferences.setValue(UPDATE_FREQUENCY, frequency);
preferences.store();
System. out .println( ">>>>>>>>>>>>>>>> Frequency updated " + frequency);
} else {
response.setRenderParameter(ERROR_MESSAGE, "Set valid frequency!" );
}
}

if (request.getParameter(SET_QUANTITY) != null ) {
if (request.getParameter(QUANTITY).trim().length() > 0
&& isDigit(request.getParameter(QUANTITY))) {
String quantity = request.getParameter(QUANTITY);
preferences.setValue(ITEM_QUANTITY, quantity);
preferences.store();
System. out .println( ">>>>>>>>>>>>>>>> Quantity updated " + quantity);
} else {
response.setRenderParameter(ERROR_MESSAGE, "Set valid quantity!" );
}
}

if (request.getParameter(UPDATE_FEEDS) != null ) {
ArrayList rssItems = ( ArrayList )rssMap. get (request.getUserPrincipal().getName());
rssItems.clear();
rssItems = RssUtil.updateRssFeeds(preferences);
System. out .println( "News fo user " + request.getUserPrincipal().getName() + " updated!" );
rssMap.put(request.getUserPrincipal().getName(), rssItems);
setLastUpdateTime(request);
}

if (request.getParameter(EDIT_BACK) != null ) {
response.setPortletMode(PortletMode.VIEW);
}



}

private static String getJspFilePath(RenderRequest request, String jspFile) {
String markup = request.getProperty( "wps.markup" );
if (markup == null )
markup = getMarkup(request.getResponseContentType());
return JSP_FOLDER + markup + "/" + jspFile + "."
+ getJspExtension(markup);
}

private static String getMarkup( String contentType) {
if ( "text/vnd.wap.wml" .equals(contentType))
return "wml" ;
else
return "html" ;
}

private static String getJspExtension( String markupName) {
return "jsp" ;
}

private static String [] addToArray( String [] array, String s) {
String [] ans = new String [array.length + 1];
System.arraycopy(array, 0, ans, 0, array.length);
ans[ans.length - 1] = s;
return ans;
}

private static String [] deleteFromArray( String [] array, String s) {
String [] ans = new String [array.length - 1];
int addIndex = 0;
for ( int i = 0; i < array.length; i++) {
if (array[i].equals(s)) {
addIndex++;
continue ;
}
ans[i - addIndex] = array[i];
}
return ans;
}

private static boolean isFound( String [] array, String s) {
boolean found = false ;
for ( int i = 0; i < array.length; i++) {
if (array[i].equals(s)) {
found = true ;
break ;
}
}
return found;
}

private static boolean isDigit( String s) {
char [] array = s.toCharArray();
for ( int i = 0; i < array.length; i++) {
if (!Character.isDigit(array[i])) {
return false ;
}
}
return true ;
}

private static Long getLastUpdateTime(PortletRequest request){
return (Long)updateTimeMap. get (request.getUserPrincipal().getName());
}

private static void setLastUpdateTime(PortletRequest request){
updateTimeMap.put(request.getUserPrincipal().getName(), new Long(System.currentTimeMillis()));
}


}

* This source code was highlighted with Source Code Highlighter .


Go through the code:

With a set of constants, everything is clear, then we create two static objects of HashMap type — the key to all values ​​of both maps is the name of a specific portal user, the value of either an ArrayList object containing Rss content or a Long object containing the time of the last update of the channels. Since the lifetime of a static portlet variable is equal to the lifetime of the portlet itself, I decided to store the data exactly in them, and not persist them somewhere, all the more I see little point in saving rss'a to any database.

Then begins what we talked about a little higher, namely, the redefinition of methods of the GenericPortlet class:

The doView method: from the request object we get an object of type PortletPreferences, from which, in turn, we can pull out the value we need or, alternatively, add our own value to it. Then we check our first map for the presence of a key with the name of the active user, if there is no such key, then we enter the key-value pairs in both maps, in one case the value is an ArrayList object, in the other - the Long object containing the last update time. Next, we check if we need a news update. Let me remind you that for all users one channel is set by default, the same number of news and the time of their update (see portlet.xml) So, if a user has visited the page with our portlet for the first time, then the channel will be immediately updated for him Received rss-content is entered into our ArrayList, which we received from the map with rss-content by user name (key). Then everything is simple - in the request we put the same ArrayList, call PortletRequestDispatcher and redirect to the Hsp page of HabraRssPortletView, which will display our rss content:

HabraRssPortletView.jsp
<%@page session="false" contentType="text/html" pageEncoding="ISO-8859-1" import="java.util.*,javax.portlet.*,ru.habrahabr.rssportlet.*" %> <br> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <br> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <br> < portlet:defineObjects /> <br><br><br> <% <br> if (request.getAttribute( "rssMap" ) != null ){<br> %> <br> <br> < c:forEach var ="item" items ="${rssMap}" > <br> < p > <br> < b >< a href ="${item.link}" > ${item.title} </ a ></ b > <br> < span > ${item.description} </ span > <br> </ p > <br> < hr > <br> </ c:forEach > <br> <br> <% <br> } else {<br> %> Error!!!! <% <br> }<br> %> <br><br> * This source code was highlighted with Source Code Highlighter .
<%@page session="false" contentType="text/html" pageEncoding="ISO-8859-1" import="java.util.*,javax.portlet.*,ru.habrahabr.rssportlet.*" %> <br> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <br> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <br> < portlet:defineObjects /> <br><br><br> <% <br> if (request.getAttribute( "rssMap" ) != null ){<br> %> <br> <br> < c:forEach var ="item" items ="${rssMap}" > <br> < p > <br> < b >< a href ="${item.link}" > ${item.title} </ a ></ b > <br> < span > ${item.description} </ span > <br> </ p > <br> < hr > <br> </ c:forEach > <br> <br> <% <br> } else {<br> %> Error!!!! <% <br> }<br> %> <br><br> * This source code was highlighted with Source Code Highlighter .
<%@page session="false" contentType="text/html" pageEncoding="ISO-8859-1" import="java.util.*,javax.portlet.*,ru.habrahabr.rssportlet.*" %> <br> <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %> <br> <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <br> < portlet:defineObjects /> <br><br><br> <% <br> if (request.getAttribute( "rssMap" ) != null ){<br> %> <br> <br> < c:forEach var ="item" items ="${rssMap}" > <br> < p > <br> < b >< a href ="${item.link}" > ${item.title} </ a ></ b > <br> < span > ${item.description} </ span > <br> </ p > <br> < hr > <br> </ c:forEach > <br> <br> <% <br> } else {<br> %> Error!!!! <% <br> }<br> %> <br><br> * This source code was highlighted with Source Code Highlighter .


On the page from the query, we get our ArrayList object, iterate it with JSTL and display our Rss content.


The doEdit method: there is nothing special here, just call PortletRequestDispatcher and go to the jsp-page HabraRssPortletEdit. All the logic is sewn in it, although it is not good. We, of course, fix all this without any problems. What is happening on this page? In the scriptlet, using the same PortletPreferences object, we get the values ​​we need, namely:

a) List of rss channels.
b) the frequency of updates.
c) Number of news from each channel.

HabraRssPortletEdit.jsp (part)
<%<br> PortletPreferences preferences = renderRequest.getPreferences();<br> if ( preferences!= null ) {<br> String frequency = ( String ) preferences.getValue(HabraRssPortlet.UPDATE_FREQUENCY, "60" );<br> String quantity = ( String ) preferences.getValue(HabraRssPortlet.ITEM_QUANTITY, "5" );<br> String [] links = ( String []) preferences.getValues(HabraRssPortlet.RSS_LINKS, null );<br>%> <br><br> * This source code was highlighted with Source Code Highlighter .
<%<br> PortletPreferences preferences = renderRequest.getPreferences();<br> if ( preferences!= null ) {<br> String frequency = ( String ) preferences.getValue(HabraRssPortlet.UPDATE_FREQUENCY, "60" );<br> String quantity = ( String ) preferences.getValue(HabraRssPortlet.ITEM_QUANTITY, "5" );<br> String [] links = ( String []) preferences.getValues(HabraRssPortlet.RSS_LINKS, null );<br>%> <br><br> * This source code was highlighted with Source Code Highlighter .
<%<br> PortletPreferences preferences = renderRequest.getPreferences();<br> if ( preferences!= null ) {<br> String frequency = ( String ) preferences.getValue(HabraRssPortlet.UPDATE_FREQUENCY, "60" );<br> String quantity = ( String ) preferences.getValue(HabraRssPortlet.ITEM_QUANTITY, "5" );<br> String [] links = ( String []) preferences.getValues(HabraRssPortlet.RSS_LINKS, null );<br>%> <br><br> * This source code was highlighted with Source Code Highlighter .

Next, we draw the fields we need, lists and buttons, inserting into them the values ​​obtained just above, and which we can then freely edit, i.e. add rss-feeds, delete them, change the values ​​of the update rate, etc.




The doHelp method: there is nothing interesting at all, we call the same PortletRequestDispatcher and redirect to the HabraRssPortletHelp page, we write anything there, although it is usually customary to write something like reference information.

The processAction method: - the heart of our portlet, this is where all the user interaction processing takes place, in our case these are all submissions from the forms that are located on the HabraRssPortletEdit page. Please note that this method uses objects of type ActionRequest and ActionResponse, unlike RenderRequest and RenderResponse, which are responsible for rendering the portlet, these two are responsible for user interaction. Processing is the simplest validation of input parameters, changing and creating preferences values. I note that the preferences validation can be put into a separate class, which should be implemented by Preferences Validator, and you should not forget to register this class in the portlet.xml descriptor. We made it a bit simpler, if the incoming parameters do not suit us, then we set the rendering parameter to the ActionResponse object, which contains an error message, this message will be displayed on the same window.


Yes, it is also very important that you can change the preferences values ​​only inside the processAction method. The most important thing to remember is to call the store () method on an object of type PortletPreferences after all changes, otherwise the changes will not be saved. I’m sorry to say that the portal keeps the preferences separately for each user, so if one user adds a few more links to his channel list, then other users still have one link, the same goes for the other preferences, they are individual for each user. . Is it really very convenient? :)



Now let's take a little look at the class responsible for RSS. I already mentioned above that I used the finished library, you can download it here , maybe it is not the best, but that is, that is :)

Rssutil.java
package ru.habrahabr.rssportlet;<br><br> public class RssUtil {<br><br> public static ArrayList updateRssFeeds(PortletPreferences preferences){<br> ArrayList allItems = new ArrayList ();<br> String [] links = preferences.getValues(HabraRssPortlet.RSS_LINKS, null );<br> int quantity = Integer.parseInt(preferences.getValue(HabraRssPortlet.ITEM_QUANTITY, "3" ));<br> for ( int i=0; i < links.length; i++){<br> String link = links[i];<br> InputStream stream = null ;<br> try {<br> URL s = new URL(link);<br> URLConnection connection = s.openConnection();<br> if (!(connection instanceof URLConnection)) { <br> throw new IllegalArgumentException(s.toExternalForm() + " is not a valid HTTP Url" );<br> }<br> stream = connection.getInputStream();<br> try {<br> RssParser parser = RssParserFactory.createDefault();<br> Rss rss = parser.parse(stream);<br> Collection items = rss.getChannel().getItems();<br> if (items != null && !items.isEmpty()) {<br> Iterator it = items.iterator();<br> int count = 0;<br> while (it.hasNext() && count < quantity){<br> Item item = (Item) it.next();<br> allItems.add(item);<br> count++;<br> }<br> }<br> } catch (RssParserException e) {<br> e.printStackTrace();<br> }<br> } catch (IOException e) {<br> e.printStackTrace();<br> }<br> }<br> return allItems;<br> }<br><br> public static boolean checkUpdateTime(Long lastUpdateTime, PortletPreferences preferences) {<br> int interval = Integer.parseInt(preferences.getValue(HabraRssPortlet.UPDATE_FREQUENCY, "60" ));<br> if (((System.currentTimeMillis() - lastUpdateTime.longValue()) / 1000) > (interval * 60))<br> return true ;<br> return false ;<br> }<br><br>}<br> <br> * This source code was highlighted with Source Code Highlighter .
package ru.habrahabr.rssportlet;<br><br> public class RssUtil {<br><br> public static ArrayList updateRssFeeds(PortletPreferences preferences){<br> ArrayList allItems = new ArrayList ();<br> String [] links = preferences.getValues(HabraRssPortlet.RSS_LINKS, null );<br> int quantity = Integer.parseInt(preferences.getValue(HabraRssPortlet.ITEM_QUANTITY, "3" ));<br> for ( int i=0; i < links.length; i++){<br> String link = links[i];<br> InputStream stream = null ;<br> try {<br> URL s = new URL(link);<br> URLConnection connection = s.openConnection();<br> if (!(connection instanceof URLConnection)) { <br> throw new IllegalArgumentException(s.toExternalForm() + " is not a valid HTTP Url" );<br> }<br> stream = connection.getInputStream();<br> try {<br> RssParser parser = RssParserFactory.createDefault();<br> Rss rss = parser.parse(stream);<br> Collection items = rss.getChannel().getItems();<br> if (items != null && !items.isEmpty()) {<br> Iterator it = items.iterator();<br> int count = 0;<br> while (it.hasNext() && count < quantity){<br> Item item = (Item) it.next();<br> allItems.add(item);<br> count++;<br> }<br> }<br> } catch (RssParserException e) {<br> e.printStackTrace();<br> }<br> } catch (IOException e) {<br> e.printStackTrace();<br> }<br> }<br> return allItems;<br> }<br><br> public static boolean checkUpdateTime(Long lastUpdateTime, PortletPreferences preferences) {<br> int interval = Integer.parseInt(preferences.getValue(HabraRssPortlet.UPDATE_FREQUENCY, "60" ));<br> if (((System.currentTimeMillis() - lastUpdateTime.longValue()) / 1000) > (interval * 60))<br> return true ;<br> return false ;<br> }<br><br>}<br> <br> * This source code was highlighted with Source Code Highlighter .
package ru.habrahabr.rssportlet;<br><br> public class RssUtil {<br><br> public static ArrayList updateRssFeeds(PortletPreferences preferences){<br> ArrayList allItems = new ArrayList ();<br> String [] links = preferences.getValues(HabraRssPortlet.RSS_LINKS, null );<br> int quantity = Integer.parseInt(preferences.getValue(HabraRssPortlet.ITEM_QUANTITY, "3" ));<br> for ( int i=0; i < links.length; i++){<br> String link = links[i];<br> InputStream stream = null ;<br> try {<br> URL s = new URL(link);<br> URLConnection connection = s.openConnection();<br> if (!(connection instanceof URLConnection)) { <br> throw new IllegalArgumentException(s.toExternalForm() + " is not a valid HTTP Url" );<br> }<br> stream = connection.getInputStream();<br> try {<br> RssParser parser = RssParserFactory.createDefault();<br> Rss rss = parser.parse(stream);<br> Collection items = rss.getChannel().getItems();<br> if (items != null && !items.isEmpty()) {<br> Iterator it = items.iterator();<br> int count = 0;<br> while (it.hasNext() && count < quantity){<br> Item item = (Item) it.next();<br> allItems.add(item);<br> count++;<br> }<br> }<br> } catch (RssParserException e) {<br> e.printStackTrace();<br> }<br> } catch (IOException e) {<br> e.printStackTrace();<br> }<br> }<br> return allItems;<br> }<br><br> public static boolean checkUpdateTime(Long lastUpdateTime, PortletPreferences preferences) {<br> int interval = Integer.parseInt(preferences.getValue(HabraRssPortlet.UPDATE_FREQUENCY, "60" ));<br> if (((System.currentTimeMillis() - lastUpdateTime.longValue()) / 1000) > (interval * 60))<br> return true ;<br> return false ;<br> }<br><br>}<br> <br> * This source code was highlighted with Source Code Highlighter .


The class consists of two static methods, one of which actually receives Rss-content from existing channels, and the other checks whether it is time to update. Nothing complicated :)

Well, that seems to be all. All that was said here is my personal IMHO. If someone has any questions - ask in comments :) All code and war-archive can be taken as an attachment :)

Ps. Yes, and sorry for System.out.println () in the code, left for clarity.

Source: https://habr.com/ru/post/45927/


All Articles