DWR упрощает обмен сообщениями между портлетами с помощью Ajax

Java-портлеты и Ajax прекрасно подходят для разработки Web-приложений

Многие разработчики стремятся использовать технологии Ajax, чтобы улучшить работу пользователей с Web-приложениями, однако программирование в Ajax может быть непростой задачей. Библиотека с открытыми исходными кодами Direct Web Remoting (DWR) может упростить разработку в Ajax для Java™-программистов, автоматически преобразуя Java-классы в классы JavaScript. В этой статье вы узнаете, как использовать DWR и портлеты, совместимые с JSR-168, для простого и быстрого построения Ajax приложений.

Сами Салкосуо, архитектор программного обеспечения, IBM

Сами Салкосуо (Sami Salkosuo) работает в IBM с 1999 г. Он главным образом занимается Java-программированием и является Sun-сертифицированным Java-программистом, IBM-сертифицированным разработчиком решений для XML и подобных технологий, и IBM-сертифицированным разработчиком решений для IBM WebSphere Portal. Помимо технологии Java, у него также есть опыт работы с Python, Fortran, LabVIEW, Visual Basic, LISP, Perl и PHP.



14.06.2007

Портлеты - это основанные на Java-платформе Web-приложения для порталов. JSR-168 (стандарт Java Community Process для разработки портлетных приложений) стандартизирует управление жизненным циклом портлета, контракты (набор определенных условий) для портлетных контейнеров, пакетирование, развертку и другие аспекты, относящимся к порталам.

Асинхронный JavaScript + XML, или Ajax, - это техника для разработки богатых интерактивных Web-приложений. Ajax использует сочетание XML, HTML, DHTML, JavaScript и DOM.

Портлеты и Ajax, похоже, идеально подходят друг другу, так как оба стараются использовать Web-браузер как инструмент, представляющий пользовательский интерфейс. Простой способ их совмещения с технологией Java - использование библиотеки DWR. DWR - это библиотека Java, с открытым исходным кодом с лицензией Apache, для создания Web-приложений, основанных на Ajax. Основная цель DWR - скрыть детали Ajax от разработчика. Вы используете простые старые Java-объекты (POJO) со стороны сервера, а DWR динамически генерирует замещающие функции JavaScript, так что при разработке со стороны клиента при помощи JavaScript, кажется, что JavaBeans вызываются напрямую. Основным компонентом DWR является Java-сервлет, который обрабатывает вызовы от браузера к серверу.

В этой статье используется DWR для создания примера приложения Ajax, основанного на трех портлетах. Мы покажем вам, как интегрировать DWR в портлетные приложения, но не будем вдаваться в подробности того, как DWR работает внутри; вы можете найти дополнительную информацию о библиотеке на Web-сайте проекта и на страницах developerWorks (см. подробности в Ресурсах). Для создания описываемого приложения вам понадобится платформа Java версии 1.3 (или более поздняя) и среда портала, совместимая с JSR-168. При разработке и проверке этого кода использовалась среда, состоящая из IBM Rational Application Developer V6.0, портала Apache Jetspeed 2.0 и Java 5.0.

Прежде чем начать, вы также должны быть знакомы с портлетами и разработкой в Ajax. Если вы хотите больше узнать об этих технологиях, обратитесь к разделу Ресурсы в конце статьи ниже. Вы можете загрузить полный код примера приложения, включая DWR, из раздела Загрузка.

Создание примера приложения для обмена сообщениями между портлетами

Пример нашего приложения имеет три портлета: Orders, Order Details и Customer Details; рисунок 1 показывает образец приложения:

Рисунок 1. Пример приложения
Пример приложения

Портлет Orders содержит список заказов. Когда пользователь выбирает номер заказа, портлет отсылает номер заказа в портлеты Order Details и Customer Details, которые затем отображают необходимый заказ и информацию о пользователе.

Настройка среды разработки

Прежде чем разрабатывать портлеты, вам нужно настроить среду разработки и DWR. Мы использовали IBM Rational Application Developer V6.0 со встроенной поддержкой разработки Java-портлетов, но подойдет и любая другая среда разработки. Проделайте следующие шаги для начала работы:

  1. Создайте новый портлетный проект в соответствии с JSR-168. Назовите проект InterPortletMessaging, но пока не создавайте никаких портлетов.
  2. Загрузите dwr.jar (версия 1.1; см. ссылку в Ресурсах). Добавьте dwr.jar к своему проекту в директории /WebContent//WEB-INF/lib.
  3. Откройте файл web.xml и скопируйте туда код из листинга 1, чтобы добавить DWR сервлет к приложению. Этот сервлет используется в фоновом режиме для обработки запросов и ответов, отсылаемых обратно в браузер.

    Листинг 1. Сервлет DWR
    <servlet>
    
      <servlet-name>dwr-invoker</servlet-name>
      <display-name>DWR Servlet</display-name>
      <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
      <init-param>
        <param-name>debug</param-name>
        <param-value>true</param-value>
      </init-param>
    </servlet>
    
    <servlet-mapping>
      <servlet-name>dwr-invoker</servlet-name>
      <url-pattern>/dwr/*</url-pattern>
    </servlet-mapping>
  4. Создайте файл с именем dwr.xml в директории WEB-INF, используя код из листинга 2. Это - конфигурационный файл DWR, который сообщает контейнеру, какие классы доступны в JavaScript. DWR читает этот XML-файл и динамически генерирует код JavaScript для отображения классов Java и JavaScript, что позволяет вам добавлять функциональность этих Java-классов в браузер. Фактически вы еще не создали Java-класс, на который ссылается листинг 2, однако мы незамедлительно этим займемся.

    Листинг 2. dwr.xml
    <!DOCTYPE dwr PUBLIC
        "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
        "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
    
    <dwr>
      <allow>
        <create creator="new" javascript="MessagingBean">
          <param name="class" value="msg.MessagingBean"/>
        </create>
      </allow>
    </dwr>

Теперь вы можете использовать DWR в своих портлетах. Тем не менее, прежде чем вы создадите примеры портлетов, вы должны создать bean-компонент обмена сообщениями и модель bean-компонента базы данных, которые послужат серверным приложением для образца приложения.

Создание MockupDB и MessagingBean

MockupDB, показанный в листинге, является одноэлементным классом, который симулирует базу данных заказов клиента. Все заказы в этом классе жестко запрограммированы. Реальное приложение, возможно, использовало бы относительную систему базы данных, однако этого примера достаточно для наших целей.

Листинг 3. MockupDB
package db;

import java.util.Hashtable;
import java.util.Map;

public class MockupDB {

  private static MockupDB instance=new MockupDB();

  private String[] orders=new String[4];
  private Map orderDetails=new Hashtable();
  private Map customerDetails=new Hashtable();

  private MockupDB()
  {
    String ordStart="ORD";
    orders[0]=ordStart+"000408015";
    orders[1]=ordStart+"001600023";
    orders[2]=ordStart+"000042000";
    orders[3]=ordStart+"011235813";

    orderDetails.put(orders[0],"1. WebSphere Everyplace Connection Manager<br/>"+
                     "2. WebSphere Portal");
    orderDetails.put(orders[1],"1. DB2 Universal Database<br/>2. DB2 Everyplace");
    orderDetails.put(orders[2],"1. Tivoli Access Manager for e-business <br/>2."+
                     "Tivoli Directory Integrator");
    orderDetails.put(orders[3],"1. IBM System z9<br/>2. IBM System p5 550 Express");

    customerDetails.put(orders[0],"<b>Systems and Technology Group</b><br/>"+
                        "Some Road<br/>Finland");
    customerDetails.put(orders[1],"<b>Global Financing</b><br/>Another Street"+
                        "<br/>Finland");
    customerDetails.put(orders[2],"<b>Software</b><br/>Yet Another Road"+
                        "<br/>Finland");
    customerDetails.put(orders[3],"<b>Global Services</b><br/>Still Another "+
                        "Street<br/>Finland");
  }
    
  public static MockupDB getInstance()
  {
    return instance;
  }
    
  public String[] getOrders()
  {
    return orders;
  }
    
  public String getOrderDetails(String orderNro)
  {
    return (String)orderDetails.get(orderNro);
  }

  public String getCustomerDetails(String orderNro)
  {
    return (String)customerDetails.get(orderNro);
  }
}

MessagingBean, показанный в листинге 4, - это простой POJO с двумя методами, которые получают номер заказа, а возвращают детали заказа и информацию о клиенте, соответственно. MessagingBean получает детали из MockupDB.

Листинг 4. MessagingBean
package msg;

import javax.servlet.http.HttpSession;

import db.MockupDB;

public class MessagingBean {

  public MessagingBean()
  {        
  }
    
  public String getOrderDetails(String orderNumber,HttpSession httpSession)
  {
    String orderDetails=MockupDB.getInstance().getOrderDetails(orderNumber);
    httpSession.setAttribute("orderDetailsOrderNumber",orderNumber);
    httpSession.setAttribute("orderDetails",orderDetails);
    return orderDetails;
  }

  public String getCustomerDetails(String orderNumber,HttpSession httpSession)
  {
    String customerDetails=MockupDB.getInstance().getCustomerDetails(orderNumber);
    httpSession.setAttribute("customerDetailsOrderNumber",orderNumber);
    httpSession.setAttribute("customerDetails",customerDetails);
    return customerDetails;
  }
}

MessagingBean также добавляет детали заказа и информацию о клиенте в HttpSession.

javaScriptFunctions.jsp

javaScriptFunctions.jsp импортирует библиотеку JavaScript из DWR (engine.js) и динамически созданную библиотеку MessagingBean.js. Отметим, что MessagingBean.js использует то же имя, что и Java-bean, представленный в dwr.xml (листинг 2); фактически, DWR генерирует MessagingBean.js. Фреймворк (framework) DWR использует библиотеку engine.js; как разработчику, вам обычно не нужно беспокоиться о прямом ее использовании.

Как можно увидеть из листинга 5, функции sendOrderNr() делают вызов к функциям MessagingBean, определенным в листинге 4. DWR автоматически добавляет HttpSession к вызову метода. Последний параметр в функции JavaScript - это функция callback. Этот JSP включается в JSP портлета, который вы создадите далее.

Листинг 5. javaScriptFunctions.jsp
<%@ page contentType="text/html" 
import="java.util.*,javax.portlet.*,interportletmessagingusingajax.*" %>
<%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
<portlet:defineObjects/>

<SCRIPT type="text/javascript"
	src='<%= renderResponse.encodeURL(renderRequest.getContextPath() +
	"/dwr/interface/MessagingBean.js") %>'> 
</SCRIPT>

<SCRIPT type="text/javascript"
	src='<%= renderResponse.encodeURL(renderRequest.getContextPath() + 
	"/dwr/engine.js") %>'> 
</SCRIPT>

<SCRIPT type="text/javascript">

function <portlet:namespace />sendOrderNr(orderNr)
{
document.getElementById("orderDetailsOrderNumber").innerHTML=orderNr;
document.getElementById("customerDetailsOrderNumber").innerHTML=orderNr;
MessagingBean.getOrderDetails(orderNr,<portlet:namespace />showOrderDetails);
MessagingBean.getCustomerDetails
(orderNr,<portlet:namespace />showCustomerDetails);

return false;
}

function <portlet:namespace />showOrderDetails(orderDetails)
{
document.getElementById("orderDetails").innerHTML=orderDetails;
return false;
}

function <portlet:namespace />showCustomerDetails(customerDetails)
{
document.getElementById("customerDetails").innerHTML=customerDetails;
return false;
}
</SCRIPT>

Создание портлетов

Теперь, когда у вас есть функции серверного приложения и замещающие (proxy) функции, вы можете разработать сами портлеты. Все три портлета используют один и тот же базовый код; разница только в имени JSP, которое использует каждый портлет.

  1. Создайте новый портлет, используя код из листинга 6, и назовите его Orders:

    Листинг 6. Orders.java
    package interportletmessagingusingajax;
    
    import java.io.*;
    
    import javax.portlet.*;
    
    public class Orders extends GenericPortlet {
    
      // JSP folder name
      public static final String JSP_FOLDER = "/interportletmessagingusingajax/jsp/";
    
      // JSP file name to be rendered on the view mode
      public static final String VIEW_JSP = "OrdersView";         
    
    
      public void init(PortletConfig config) throws PortletException{
        super.init(config);
      }
    
      public void doView(RenderRequest request, RenderResponse response) 
        throws PortletException, IOException {
        // Set the MIME type for the render response
        response.setContentType(request.getResponseContentType());
    
        // Invoke the JSP to render
        PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher( 
          getJspFilePath(request, VIEW_JSP));
        rd.include(request,response);
    
        //this is workaround for portletsession sharing between
        //servlets and portlets
        //see http://weblogs.java.net/blog/wholder/archive/2005/02/session_session.html
        //and http://mail-archives.apache.org/mod_mbox/portals-pluto-dev/200502.mbox/%3Ca
        //2519328f3ba1d1eddfc33c924b6805d@umich.edu%3E
        //
        PortletRequestDispatcher rd2 = getPortletContext().getRequestDispatcher("/dwr/");
        rd2.include(request, response);
    
      }
    
      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";
        return "html";
      }
    
      private static String getJspExtension(String markupName) {
        return "jsp";
      }
    }
  2. Создайте и откройте OrdersView.jsp (в директории interportletmessagingusingajax/jsp/html) и добавьте в него код из листинга 7:

    Листинг 7. OrdersView.jsp
    <%@ page contentType="text/html" 
    import="java.util.*,javax.portlet.*,interportletmessagingusingajax.*" %>
    <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
    <portlet:defineObjects/>
    <jsp:include page="javascriptFunctions.jsp" />
    
    <DIV style="margin: 6px">
    
    <H4 style="margin-bottom: 3px">Orders</H4>
    <table cellspacing="0" cellpadding="5" border="1">
    <% db.MockupDB database= db.MockupDB.getInstance();
    
    String[] orders=database.getOrders();
    for(int i=0;i<orders.length;i++)
    {
    %>
    <tr>
    
    <td><%="000000000"+String.valueOf(i+1) %></td>
    <td><a href="" onclick="return <portlet:namespace />sendOrderNr('<%=
      orders[i]%>');"><%=orders[i]%></a></td>
    </tr>
    <%
    }
     %>
    
    </table>
    </DIV>
  3. Второй портлет - это OrderDetailsPortlet.java. Используйте для этого портлета код из листинга 6 и поменяйте значение переменной VIEW_JSP на OrdersDetailsPortletView.jsp. Код для этого JSP в листинге 8:
    Листинг 8. OrdersDetailsPortletView.jsp
    <%@ page contentType="text/html" 
    import="java.util.*,javax.portlet.*,interportletmessagingusingajax.*" %>
    <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
    <portlet:defineObjects/>
    
    <DIV style="margin: 6px">
    
    <H4 style="margin-bottom: 3px">Order details</H4>
    
    <table cellspacing="0" cellpadding="5" border="1">
    <tr>
    <th>Order number</th>
    <th>Order details</th>
    </tr>
    
    <tr>
    <%
    String orderDetailsOrderNumber=
    (String)renderRequest.getPortletSession().getAttribute(
      "orderDetailsOrderNumber",PortletSession.APPLICATION_SCOPE);
    String orderDetails=(String)renderRequest.getPortletSession().getAttribute(
      "orderDetails",PortletSession.APPLICATION_SCOPE);
    
    if(orderDetailsOrderNumber==null)
    {
    orderDetailsOrderNumber="";
    }
    
    if(orderDetails==null)
    {
    orderDetails="";
    }
    %>
    <td><div id="orderDetailsOrderNumber"
    ><%=orderDetailsOrderNumber%>
    </div></td>
    <td><div id="orderDetails"><%=orderDetails%></div></td>
    </tr>
    
    
    </table>
    </DIV>
  4. Третий портлет - это CustomerDetailsPortlet.java. Используйте для этого портлета код из листинга 6 и измените значение переменной VIEW_JSP на CustomerDetailsPortletView.jsp. Код для этого JSP в листинге 9:
    Листинг 9. CustomerDetailsPortletView.jsp
    <%@ page contentType="text/html" 
    import="java.util.*,javax.portlet.*,interportletmessagingusingajax.*" %>
    <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
    <portlet:defineObjects/>
    
    <%
    %>
    
    <DIV style="margin: 6px">
    
    <H4 style="margin-bottom: 3px">Customer details</H4>
    <table cellspacing="0" cellpadding="5" border="1">
    <tr>
    <th>Order number</th>
    <th>Customer details</th>
    </tr>
    
    <tr>
    <%
    String customerDetailsOrderNumber=
      (String)renderRequest.getPortletSession().getAttribute(
      "customerDetailsOrderNumber",PortletSession.APPLICATION_SCOPE);
    String customerDetails=(String)renderRequest.getPortletSession().getAttribute(
      "customerDetails", PortletSession.APPLICATION_SCOPE);
    
    if(customerDetailsOrderNumber==null)
    {
    customerDetailsOrderNumber="";
    }
    
    if(customerDetails==null)
    {
    customerDetails="";
    }
    %>
    <td><div id="customerDetailsOrderNumber"><%=customerDetailsOrderNumber%>
    </div></td>
    <td><div id="customerDetails"><%=customerDetails%></div></td>
    </tr>
    
    </table>
    </DIV>

Теперь образец приложения готов. Следующим нашим шагом будет упаковка портлета в WAR файл и тестирование его на портале Apache Jetspeed.


Тестирование образца приложения

В этом разделе вы увидите образец приложения в действии. Сначала вы создадите портлетный WAR файл и установите его на портале Jetspeed. Далее, вы добавите портлеты к порталу и посмотрите, как они работают. Вы будете встраивать их в одну страницу, однако вы могли бы поместить их и на несколько страниц, если бы это было нужно; закулисный механизм все равно бы работал.

Установка портлетного приложения в Jetspeed

Установите портлетный WAR файл в Jetspeed, скопировав WAR файл в директорию <Jetspeed install dir>/webapps/jetspeed/WEB-INF/deploy. Jetspeed автоматически установит портлет и он будет готов к использованию.

Выполните следующие шаги, чтобы добавить новую страницу к порталу Jetspeed:

  1. Зайдите на портал Jetspeed и зарегистрируйтесь как администратор.
  2. Выберите значок Edit в правом углу и добавьте новую страницу с именем Inter-Portlet Messaging, как показано на рисунке 2:

    Рисунок 2. Добавление новой страницы
    Добавление новой страницы
  3. Выберите страницу Inter-Portlet Messaging и нажмите на значок Edit. Затем выберите значок Add a portlet, чтобы добавить портлеты на страницу. Выберите портлеты Orders, Order Details и Customer Details и нажмите Select portlets, чтобы добавить портлеты к портальной странице. После выполнения этого, страница должна выглядеть так, как на рисунке 3:
    Рисунок 3. Портлеты на странице
    Портлеты на странице

Портлеты

Портлет Orders, показанный на рисунке 4, перечисляет заказы:

Рисунок 4. Портлет Orders
Портлет Orders

Когда вы выбираете номер заказа, остальные портлеты показывают детали конкретного заказа. Портлет Customer Details отображает данные клиента, как показано на рисунке 5. Эта информация извлекается из MockupDB.

Рисунок 5. Портлет Customer Details
Портлет Customer Details

Портлет Order Details также представляет информацию, извлеченную из MockupDB, как вы можете видеть на рисунке 6:

Рисунок 6. Портлет Order Details
Портлет Order Details

При желании можно вернуться и добавить один или больше портлетов на разные страницы. Вы увидите, что ваши портлеты не обязательно должны располагаться на одной странице, потому что содержимое портлета хранится в сеансе пользователя.


Заключение

В этой статье представлен один из способов достижения связи между портлетами с использованием Ajax. Ajax - очень мощная технология для разработки интерактивных Web-страниц, и портлеты, оснащенные Ajax, существенно улучшают свои пользовательские качества, ликвидируя типичную для порталов задержку при обработке запросов и ответов.

Вы можете использовать образец кода из этой статьи в качестве отправной точки в разработке своих собственных приложений; также этот код показывает, как DWR расширяет модель программирования Java для Web-браузеров. При помощи DWR кажется, как будто JavaBeans доступны в браузере. DWR упрощает вашу работу, скрывая почти все детали Ajax, и позволяет вам концентрироваться на конкретной задаче, не отвлекаясь на тонкости разработки в Ajax.


Загрузка

ОписаниеИмяРазмер
Исходная программаj-ajaxportlet.war3MB

Ресурсы

Научиться

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, WebSphere
ArticleID=230833
ArticleTitle=DWR упрощает обмен сообщениями между портлетами с помощью Ajax
publish-date=06142007