In this recipe, we will create a stock watch list that utilizes jQuery to retrieve updated stock prices without refreshing the page.
The following are required for this recipe:
To create a portlet that uses jQuery for Ajax calls:
gatein.cookbook
, Artifact Id: chapter10-jquery
, 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>
WatchlistPortlet
that extends javax.portlet.GenericPortlet
within a package named gatein.cookbook.chapter10
.private
variable to WatchlistPortlet
called watchedStocks
of type String[]
and set it to null
.private
variable to WatchlistPortlet
called watchedStockPrice
of type BigDecimal[]
and set it to null
.init
within WatchlistPortlet
with the following content:@Override public void init() throws PortletException { initStockList(); initStockPrices(); }
initStockList
within WatchlistPortlet
with the following content:private void initStockList() { watchedStocks = new String[3]; watchedStocks[0] = "IBM:International Business Machines Corp."; watchedStocks[1] = "REV:Revlon"; watchedStocks[2] = "RHT:Red Hat"; }
initStockPrices
within WatchlistPortlet
with the following content:private void initStockPrices() { watchedStockPrice = new BigDecimal[3]; watchedStockPrice[0] = new BigDecimal(32.10).setScale(2, RoundingMode.HALF_UP); watchedStockPrice[1] = new BigDecimal(12.54).setScale(2, RoundingMode.HALF_UP); watchedStockPrice[2] = new BigDecimal(57.01).setScale(2, RoundingMode.HALF_UP); }
src/main/webapp/js
folder of the project.display
within WatchlistPortlet
as follows:@RenderMode(name = "view") public void display(RenderRequest request, RenderResponse response) throws PortletException, IOException { request.setAttribute("watchList", watchedStocks); request.setAttribute("prices", watchedStockPrice); getPortletContext().getRequestDispatcher("/watchlist.jsp").include(request, response); }
updateStockPrice
within WatchlistPortlet
as follows:protected String updateStockPrice(int index) { Float increment = new Random(watchedStockPrice[index].longValue()).nextFloat(); BigDecimal newValue = watchedStockPrice[index].add(new BigDecimal(increment.doubleValue())); newValue = newValue.setScale(2, RoundingMode.HALF_UP); watchedStockPrice[index] = newValue; return newValue.toString(); }
serveResource
within WatchlistPortlet
as follows:@Override public void serveResource(ResourceRequest request, ResourceResponse response) throws PortletException, IOException { String resourceID = request.getResourceID(); PrintWriter writer = response.getWriter(); if ("refreshPrice".equalsIgnoreCase(resourceID)) { int index = Integer.parseInt(request.getParameter("stock")); writer.write(updateStockPrice(index)); } writer.flush(); writer.close(); }
watchlist.jsp
in the src/main/webapp
folder of the project.watchlist.jsp
:<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %> <%@ page import="java.math.BigDecimal" %> <portlet:defineObjects/> <div class="portlet-section-header">Stock Watchlist</div> <br/> <div class="portlet-section-body"> <% String[] stocks = (String[])renderRequest.getAttribute("watchList"); BigDecimal[] prices = (BigDecimal[])renderRequest.getAttribute("prices"); %> <table border="1"> <thead class="portlet-table-header"> <tr> <th>Ticker</th> <th>Company</th> <th>Stock Price</th> <th></th> </tr> </thead> <tbody class="portlet-table-body"> <% int count = 0; for (String stock: stocks) { if (null != stock) { String[] split = stock.split(":"); %> <portlet:resourceURL var="refreshPriceUrl" id="refreshPrice"> <portlet:param name="stock" value="<%= Integer.toString(count) %>" /> </portlet:resourceURL> <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"><span id="<portlet:namespace/>price<%= count %>"><%= prices[count] %></span></td> <td align="center" class="portlet-table-text"><a class="portlet-font-dim" href="#" onclick="<portlet:namespace/>updatePrice('${refreshPriceUrl}', <%= count++ %>);">Refresh Price</a></td> </tr> <% } } %> </tbody> </table> </div> <br/> <script type="text/javascript" src="<%= renderRequest.getContextPath() %>/js/jquery-1.7.2.min.js"></script> <script type='text/javascript'> function <portlet:namespace/>updatePrice(updatePriceUrl, index) { $.ajax( { type: "POST", url: updatePriceUrl, cache: false, success: function (data) { $('#<portlet:namespace/>price'+index).html(data); } } ); } </script>
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-jQuery</portlet-name> <portlet-class>gatein.cookbook.chapter10.WatchlistPortlet</portlet-class> <supports> <mime-type>text/html</mime-type> <portlet-mode>view</portlet-mode> </supports> <portlet-info> <title>WatchList - jQuery portlet</title> </portlet-info> </portlet> </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>
>mvn clean package
chapter10-jquery-1.0.0.SNAPSHOT.war
, from the target
folder into the deployment folder of where you unpacked the GateIn installation.Watchlist-jQuery
portlet to it. The Watchlist-jQuery
portlet can be found under the Chapter10-jquery
category.Steps 7 and 8 initialize the array of stocks that are being watched and what the initial price for each of them is when the portlet is initialized at startup.
Step 10 handles the RenderRequest
for the portlet, by setting the array of stocks and their prices as request attributes before redirecting the request to the JSP page for display.
Step 11 defines updateStockPrice
, a method for incrementing the stock price by a random amount. This is easily modified to call a service to retrieve stock prices from a live system.
Step 12 defines serveResource
, which is where the Ajax request is handled by the portlet. The resource ID is checked to make sure the
ResourceRequest
received by the portlet is the correct one, though in practice this check is more applicable when a single portlet handles many Ajax requests through the serveResource
method. A
PrintWriter
is retrieved from the ResourceResponse
for writing the result of the request back to the browser. The stock
request parameter is retrieved and used as the index for calling updateStockPrice
.
Step 14 creates the watchlist.jsp
, which handles all the portlet content display. It uses the <portlet:resourceURL>
tag to create a self-referencing URL for retrieving resources from the portlet. The id
attribute on the tag is set as the resource ID on the ResourceRequest
, so the value must match what we are
expecting within serveResource
, otherwise it won't successfully retrieve any content.
Within the stock table, a link is created that uses the onclick
event to trigger a JavaScript call to updatePrice
, using the resource URL that was created earlier.
At the bottom of watchlist.jsp
, the jQuery JavaScript library is loaded and the updatePrice
method is defined. It instructs jQuery to make an Ajax call using the URL passed to it, and to update the HTML element with the id
of #<portlet:namespace/>price'+index
with the data that was returned in the response. The call is specified as non-cacheable as there is no need to cache the stock price, because it is not a static resource.
In the JSP, <portlet:namespace/>
was used to generate an ID unique to this portlet to prevent ID and JavaScript name clashes with other portlets that are on the same page.
In Step 27, when Refresh Price is clicked one or more times, the entire portal page and portlet are not re-rendered at any point; it is only the single piece of HTML content that is replaced by Ajax. This happens as calling serveResource
does not result in a new RenderRequest
being issued by the portal container.
3.135.198.174