Wednesday, October 12, 2011

Consuming Web Services and IPC (Part One)

This is part one of a two part write-up, focusing initially on creating a Liferay portlet that consumes data from an external web service and presents it in the portlet as OPTION data in an HTML SELECT.  In part two, I will detect when a user selects a value from the dropdown and fire an event that another portlet will be listening for, present the data in the listener portlet.
So the purpose of this exercise is two-fold.  The first objective is to consume and present data from an external web service.  The second objective (part two) is to exercise portlet-to-portlet communication, building from this first example.
All of the environment options for developing, building, and deploying Liferay portlets, can be a bit overwhelming, when you factor in using a development environment like Eclipse, but Liferay makes it pretty simple with its plugin SDK for Eclipse to build portlets.  Since many people have already solved setting up the development environment, I’m going to assume that part is already out of the way, and get right into the creation of the portlet, using Eclipse’s SDK plugin.  (If you need to configure your environment, google “+Liferay +Eclipse +plugin +sdk”, and you should be able to find the resources you need.
Another note: I am using Liferay 6.0.5 and the 6.0.5 version of the plugins SDK.  There may be variations, if you are using other versions of Liferay, or the plugins SDK.
Let’s get started…  This first exercise will be to create a portlet that calls a web service, over the internet, and retreives a list of countries.  Those countries will be displayed in an HTML SELECT dropdown.
1.       Using Liferay’s SDK plugin for Eclipse, create a new “Liferay Plug-in Project”
2.       Fill in the fields for the Plug-in Project as follows
·         Project name = “country-list”
·         Display name = “country-list”
·         Liferay Plug-ins SDK = “plugins-sdk-6.0.5”
·         Liferay Portal Runtime = “Liferay v6.0 CE (Tomcat 6)”               (you may have to configure this first)
·         Plug-in type = Portlet

3.       Click Next
4.       Choose the portlet framework
·         Select the Liferay MVC portlet framework radio button
·         Check the “Create custom portlet class” checkbox

5.       Click Next
6.       Define the portlet class
·         Portlet class = “CountryPortlet”
·         Java package =” com.mycompany.portlets”
·         Superclass = “com.liferay.util.bridges.mvc.MVCPortlet”

7.       Click Next
8.       Specify the portlet deployment descriptor details
·         Name = “countryportlet”
·         Display name = “CountryPortlet”
·         Title = “CountryPortlet”
·         Select the “Edit” Portlet Modes
·         Ensure the “Create JSP file” checkbox is selected
·         JSP folder = “/html/countryportlet

9.       Click Next
10.       Add additional deployment descriptor details
·         Makd sure the CSS classname = “countryportlet-portlet”
·         Select the “Sample” Category

11.       Click Next
12.       Click Finish to create the new country-list-portlet

13.       In Eclipse, the final check should be to ensure that there is a “Build Successful” message in the console and that the “country-list-portlet” project/package is created in your Package Explorer

I have configured my Eclipse environment to have Tomcat running as a server in Eclipse.  From there, you can click on the Tomcat Server and add the portlet as a configured resource to the server.  This is slick because as you make changes to your portlet, it automatically hot-deploys your portlet for you, and you can do iterative development and testing.  (See the screenshots below for the proper settings)
In order to proceed to the next steps, you will need to ensure that your portlet is “deployed”.  Either deploy it manually, or setup your portlet as a resource in your Eclipse/Tomcat server, and it will be deployed for you.  I am moving forward, assuming you are able to deploy the portlet.


With the portlet now deployed, you should be able to open a browser: http://localhost:8080/ and get to a default Liferay page.  You will need to signin with Admin privileges, so you can add a portlet to the page.  You should find the portlet in the “Sample” category, based on how we configured the portlet using the plug-in SDK.

Now that our portlet exists, let’s get to the code, and make it useful!  First you should know that we are going to receive a list of countries from a free web service, available on the internet.  You can click on the following URL to see contents of the data we will be retreiving: http://www.webservicex.net/country.asmx/GetCountries
Open up and edit the CountryPortlet.java file, located in the com.mycompany.portlets package we created.  Edit the source to look as follows.
Note: My corporate firewall would not let me invoke the web services, from within my Java application, so I had to set System properties to enable me to get out through the firewall.  This may or may not be necessary for you.  I have commented these lines out for you to use, if you need to.
package com.mycompany.portlets;

import java.io.*;
import java.net.*;
import java.util.HashMap;
import java.util.Map;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.ProcessAction;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.util.bridges.mvc.MVCPortlet;

/**
 * Portlet implementation class CountryPortlet
 */
public class CountryPortlet extends MVCPortlet {

      @Override
      public void doView(RenderRequest renderRequest, RenderResponse renderResponse) throws IOException, PortletException {

            Map countryList = new HashMap();

            try {
                  // System.setProperty("http.proxyType", "4");
                  // System.setProperty("http.proxySet", "true");
                  // System.setProperty("http.proxyHost", "your proxy host");
                  // System.setProperty("http.proxyPort", "your proxy port");
                  // System.setProperty("http.proxyDomain", "your proxy domain");

                  URL wsURL = new URL("http://www.webservicex.net/country.asmx/GetCountries");
                  URLConnection uc = wsURL.openConnection();

                  BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream()));
                  String inputLine;

                  String xmlResult = "";
                  while ((inputLine = in.readLine()) != null) {
                        inputLine = inputLine.replaceAll("&lt;", "<");
                        inputLine = inputLine.replaceAll("&gt;", ">");

                        xmlResult += inputLine;
                  }
                  in.close();

                  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                  DocumentBuilder db = dbf.newDocumentBuilder();
                  InputSource is = new InputSource(new StringReader(xmlResult));
                  Document doc = db.parse(is);

                  NodeList nodes = doc.getElementsByTagName("Name");

                  for (int i = 0; i < nodes.getLength(); i++) {
                        String node = nodes.item(i).getChildNodes().item(0).getTextContent();
                        countryList.put(i, node);
                  }
            }
            catch (Exception e) {
                  System.out.println("Error: " + e.getMessage());
            }

            renderRequest.setAttribute("out-string", countryList);
            super.doView(renderRequest, renderResponse);
      }

}

Next, we need to edit the view.jsp file, located in the docroot/html/countryportlet folder.  Edit the source to look as follows.
<%@page import="java.util.*"%>
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %>

<portlet:defineObjects />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.0/jquery.min.js?ver=1.4.0"></script>

Country     <select id="countryList">
<option value="0">-- Please Select a Country --</option>
<%
      // String stringFromMyPortlet = (String)renderRequest.getAttribute("out-string");
      Map countryList = (HashMap)renderRequest.getAttribute("out-string");
     
      Iterator it = countryList.entrySet().iterator();    
      while (it.hasNext()) {        
            Map.Entry pairs = (Map.Entry)it.next(); 
%>
            <option value="<%= pairs.getKey() %>"><%= pairs.getValue() %></option>
<%    } %>

</select>

If your Liferay server resources are configured, you should simply be able to save your source files, and it will hot deploy them for you.  My console output looks as follows:

Refreshing the browser, with your portlet on it should result in something like this:

That's it!  Hopefully I didn't miss anything obvious, or make too many assumptions about what you may already know, or how your environment might be configured.  If this posting was valuable, please let me know, and I will follow-up with part two, making Inter-Portlet Communication work, leveraging the example we just created.

8 comments:

  1. Thumbs up! Looking forward to part 2

    ReplyDelete
  2. Thanks 13thbit!

    I was beginning to wonder if this site drew any visitors at all, or if what I had posted was simply a demonstration of being the Master of the Obvious.

    I will try and add part 2 in the next couple of weeks, once my current workload slows to a simmer. I think IPC is pretty cool and opens the door to a lot of UI possibilites in the Portal space, specifically around the concept of reusable components/portlets.

    Thanks so much for the reply!
    Todd

    ReplyDelete
  3. Thank you...Looking forward for Part 2

    ReplyDelete
  4. Too much good... waiting for PART 2....

    ReplyDelete
  5. Here I understood how to consume web services if it is xml. I have a requirement to do the same thing what you did above using "JSON". Can u help me please....?
    Please send the code to : shibu.k@quadra.in

    ReplyDelete
  6. Very nice tutorial..............

    ReplyDelete