Create two portlets, each in a separate portlet application, one to search for stocks and the other to show a list of stocks in a watchlist. When a stock is retrieved in the first portlet it will provide the option to watch that stock, and if clicked the stock will be visible within the watchlist by passing that stock through Public Render Parameters.
A portlet application equates to a single web archive, and there is no restriction on how many portlets can be within a single portlet application.
The following are required for this recipe:
To create the portlet that will search for stocks, do the following:
gatein.cookbook
, Artifact ID: chapter6-prp
, and Packaging: war
.pom.xml
, add the following dependency:<dependency> <groupId>javax.portlet</groupId> <artifactId>portlet-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency>
StockSearchPortlet
that extends javax.portlet.GenericPortlet
within a package named gatein.cookbook.chapter6
.private
variable to StockSearchPortlet
named availableStocks
of type String[]
and set it to null
.init
within StockSearchPortlet
with the following content:@Override public void init(PortletConfig config) throws PortletException { super.init(config); initStockList(); }
initStockList
within StockSearchPortlet
with the following content:private void initStockList() { availableStocks = new String[5]; availableStocks[0] = "IBM:International Business Machines Corp."; availableStocks[1] = "IBN:Icici Bank Limited"; availableStocks[2] = "REV:Revlon"; availableStocks[3] = "RHI:Robert Half International Inc."; availableStocks[4] = "RHT:Red Hat"; }
display
within StockSearchPortlet
as follows:@RenderMode(name = "view") public void display(RenderRequest request, RenderResponse response) throws PortletException, IOException { request.setAttribute("stockList", filterStocks(request)); getPortletContext().getRequestDispatcher("/stockSearch.jsp").include(request, response); }
filterStocks
within StockSearchPortlet
with the following content:private String[] filterStocks(RenderRequest request) { String filter = request.getParameter("ticker"); if (null != filter && filter.trim().length() > 0) { filter = filter.trim().toLowerCase(); StringBuffer filterStocks = new StringBuffer(60); boolean found = false; for (String stock : availableStocks) { if (stock.toLowerCase().startsWith(filter)) { if (found) { filterStocks.append(";"); } filterStocks.append(stock); found = true; } } return found == true ? filterStocks.toString().split(";") : null; } return availableStocks; }
searchStocks
within StockSearchPortlet
with the following content:@ProcessAction(name = "searchStocks") public void searchStocks(ActionRequest request, ActionResponse response) throws PortletException { response.setRenderParameter("ticker", request.getParameter("ticker")); }
addWatch
within StockSearchPortlet
with the following content:@ProcessAction(name = "watch") public void addWatch(ActionRequest request, ActionResponse response) throws PortletException { String stock = request.getParameter("stock"); response.setRenderParameter("watch_stock", stock); }
stockSearch.jsp
in the src/main/webapp
folder of the project.stockSearch.jsp
:<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %> <portlet:defineObjects/> <div class="portlet-section-header">Search Stocks</div> <br/> <div class="portlet-section-body"> <form action='<portlet:actionURL name="searchStocks"/>' method="post"> <span class="portlet-form-label">Ticker:</span> <input class="portlet-form-input-field" type="text" name="ticker"/> <input class="portlet-form-button" type="Submit" value="Search"/> </form> <br/> <% String ticker = (String)renderRequest.getParameter("ticker"); String[] results = (String[])renderRequest.getAttribute("stockList"); %> <c:choose> <c:when test="<%= results != null && results.length > 0 %>"> <table border="1"> <thead class="portlet-table-header"> <tr> <th>Ticker</th> <th>Company</th> <th></th> </tr> </thead> <tbody class="portlet-table-body"> <% for (String stock: results) { if (null != stock) { String[] split = stock.split(":"); %> <tr> <td align="center" class="portlet-table-text"><%= split[0] %></td> <td align="center" class="portlet-table-text"><%= split[1] %></td> <td align="center" class="portlet-table-text"><a class="portlet-font-dim" href="<portlet:actionURL name='watch'><portlet:param name='stock' value='<%= split[0] %>' /></portlet:actionURL>">Watch!</a></td> </tr> <% } } %> </tbody> </table> </c:when> <c:otherwise> <c:if test="<%= ticker != null && ticker.trim().length() > 0 %>"> <span>No results found for ticker: <%= ticker %></span> </c:if> </c:otherwise> </c:choose> </div> <br/>
portlet.xml
in the src/main/webapp/WEB-INF
folder of the project.portlet.xml
:<?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"> <portlet> <portlet-name>StockSearch-PRP</portlet-name> <portlet-class>gatein.cookbook.chapter6.StockSearchPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>Stock Search - PRP portlet</title> </portlet-info> <supported-public-render-parameter>watch_stock</supported-public-render-parameter> </portlet> <public-render-parameter> <identifier>watch_stock</identifier> <qname xmlns:gi="http://www.gatein.org/xml/ns/cookbook">gi:watch_stock</qname> </public-render-parameter> </portlet-app>
web.xml
in the src/main/webapp/WEB-INF
folder of the project.web.xml
:<?xml version="1.0"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd" version="2.5"> </web-app>
To create the portlet that will maintain a watch list of stocks, do the following:
gatein.cookbook
, Artifact ID: chapter6-prp-receiver
, and Packaging: war
.WatchlistPortlet
that extends javax.portlet.GenericPortlet
within a package named gatein.cookbook.chapter6
.private
variable to WatchlistPortlet
named watchedStocks
of type String[]
and set it to new String[5]
.private
variable to WatchlistPortlet
named watchedCount
of type int
and set it to 0
.displayWatchList
within WatchlistPortlet
as follows:@RenderMode(name = "view") public void displayWatchList(RenderRequest request, RenderResponse response) throws PortletException, IOException { String watchStock = (String)request.getParameter("watch_stock"); if (null != watchStock && watchStock.trim().length() > 0) { if (watchCount > 0) { boolean found = false; for (String stock : watchedStocks) { if (null != stock && stock.equals(watchStock)) { found = true; break; } } if (!found) { watchedStocks[watchCount++] = watchStock; } } else { watchedStocks[watchCount++] = watchStock; } } request.setAttribute("watchlist", watchedStocks); request.setAttribute("watchCount", watchCount); getPortletContext().getRequestDispatcher("/watchlist.jsp").include(request, response); }
watchlist.jsp
in the src/main/webapp
folder of the project.watchlist.jsp
:<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %> <portlet:defineObjects/> <div class="portlet-section-header">Stock Watchlist</div> <br/> <div class="portlet-section-body"> <% String[] stocks = (String[])renderRequest.getAttribute("watchList"); Integer watchCount = (Integer)renderRequest.getAttribute("watchCount"); %> <c:choose> <c:when test="<%= watchCount > 0 %>"> <table border="1"> <thead class="portlet-table-header"> <tr> <th>Ticker</th> </tr> </thead> <tbody class="portlet-table-body"> <% for (String stock: stocks) { if (null != stock) { %> <tr> <td align="center" class="portlet-table-text"><%= stock %></td> </tr> <% } } %> </tbody> </table> </c:when> <c:otherwise> <span>No stocks being watched at the moment.</span> </c:otherwise> </c:choose> </div> <br/>
portlet.xml
in the src/main/webapp/WEB-INF
folder of the project.portlet.xml
:<?xml version="1.0" encoding="UTF-8"?> <portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0"> <portlet> <portlet-name>WatchList-PRP</portlet-name> <portlet-class>gatein.cookbook.chapter6.WatchlistPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>WatchList - PRP portlet</title> </portlet-info> <supported-public-render-parameter>watch_stock</supported-public-render-parameter> </portlet> <public-render-parameter> <identifier>watch_stock</identifier> <qname xmlns:gi="http://www.gatein.org/xml/ns/cookbook">gi:watch_stock</qname> </public-render-parameter> </portlet-app>
>mvn clean package
chapter6-prp-1.0.0.SNAPSHOT.war
, from the target
folder into the deployment folder where you unpacked the GateIn installation.StockSearch-Prp
and WatchList-Prp
portlets as seen in Chapter 2, Managing Portal Contents Using the GUI.Steps 4, 5, and 6 create a String
array to hold a list of stocks that are available to search for, and initialize that list during the init
of the portlet. The initStockList
method could retrieve the stocks from a database or any external source, but in this case it is purely hard-coded for convenience.
Step 7 sets the list of stocks that will be displayed onto the RenderRequest
, before redirecting to the stockSearch.jsp
page. The list of stocks set on the RenderRequest
is generated in the
filterStocks
method in Step 8. It uses the value of ticker
, set in Step 9, to reduce the number of stocks returned if there are partial matches.
Step 10 sets the stock we want to watch as a render parameter of the ActionResponse
within the portlet. By default, this is only available to StockSearchPortlet
, but by adding the <public-render-parameter>
section to portlet.xml
in Step 14 the render parameter is exposed publicly under the namespace and name specified.
Step 21 retrieves the parameter value from the request, which is available because the portlet definition specifies that it supports the Public Render Parameter details configured in Step 25.
A major restriction with using Public Render Parameters is that they are only suitable for passing string parameters between portlets.
3.148.108.112