понеділок, 31 березня 2008 р.

JSR-168 Portal + JSF: Работа с настройками портлетов

копия моей статьи на Liveinternet.ru от 9 октября 2007
все порталы, удовлетворяющие спецификации JSR-168, предоставляют стандартный механизм доступа и модификации настроек портлетов, посредством реализации интерфейса javax.portlet.PortletPreferences. в данной заметке рассмотрим способ работы с настройками портлета, реализованного с помощью технологии JavaServer Faces.
быстрое решение
основным интерфейсом связи с внешней средой для JSF технологии является класс javax.faces.context.FacesContext. параметры портлета, сведения о типе портального запроса, настройках режима и вида портлета храняться в классах-реализациях интерфейса портального запроса javax.portlet.PortletRequest. сведенья о пространстве имен портлета храняться в реализациях интерфейса портального ответа javax.portlet.PortletRespose. для связи всех этих классов и получения доступа к специфическим параметраметрам и установкам портлета реализуем утилитарный класс:

public final class PorletFacesUtils {
    private PortletFacesUtils() {
    }

    public static PortletRequest getPortletRequest(FacesContext context) throws Exception {
        Object request = context.getExternalContext().getRequest();
        if (request instanceof PortletRequest) {
            return (PortletRequest) request;
        } else {
            throws new Exception(“Portlet run outside the portal environment or portlet request class is not generic”);
        }
    }

    public static PortletRequest getPortletResponse(FacesContext context) throws Exception {
        Object response = context.getExternalContext().getResponse();
        if (request instanceof PortletResponse) {
            return (PortletResponse) response;
        } else {
            throws new Exception(“Portlet run outside the portal environment or portlet response class is not generic”);
        }
    }

    public static PortletPreferences getPortletRequest(FacesContext context) throws Exception {
        PortletRequest request = getPortletRequest(context);
        return request.getPortletPreferences();
    }
}

таким образом мы реализовали своеобразный мост между JSF-приложением и специфическими классами портальной среды. далее этот класс может быть использован для работы с параметрами и настройками портлета. также класс расширен для получения других классов, специфичных для портальной среды (например javax.portlet.PortletSession, javax.portlet.PortletContext и т.п.).
риски
некоторые фреймворки JSF компонентов (например ICEfaces) использую свои врапперы портальных запросов и ответов. эти врапперы могут не являться наследниками стандартных интерфейсов, а следовательно будут ошибочно обработаны утилитарным классом. в этом случае необходимо реализовать механизм адаптера портальной среды для конкретной реализации враппера. как это сделать, я расскажу ниже.

гибкое решение
реализация гибкого механизма доступа к компонентам портальной среды позволит существенно упростить переход между различными библиотеками JSF компонентов, реализациями порталов и спецификации JSF. за основу этого решения мы возьмем функциональность утилитарного класса.
вместо утилитарного класса опишем общий интерфейс адаптера портальной среды:

public interface PortletFacesAdapter extends Serializable {
   PortletRequest getPortletRequest(FacesContext context);

   PortletResponse getPortletResponse(FacesContext context);

   PortletPreferences getPortletPreferences(FacesContext context);
}

перенесем функциональность утилитарного класса в реализацию интерфейса адаптера:

public class GenericPortletFacesAdapter {
    public PortletRequest getPortletRequest(FacesContext context) throws Exception {
        Object request = context.getExternalContext().getRequest();
        if (request instanceof PortletRequest) {
            return (PortletRequest) request;
        } else {
            throws new Exception(“Portlet run outside the portal environment or portlet request class is not generic”);
        }
    }

    public PortletRequest getPortletResponse(FacesContext context) throws Exception {
        Object response = context.getExternalContext().getResponse();
        if (request instanceof PortletResponse) {
            return (PortletResponse) response;
        } else {
            throws new Exception(“Portlet run outside the portal environment or portlet response class is not generic”);
        }
    }

    public PortletPreferences getPortletRequest(FacesContext context) throws Exception {
        PortletRequest request = getPortletRequest(context);
            return request.getPortletPreferences();
        }
}

теперь нам необходимо реализовать JSF managed bean, который будет поддерживать механизм адаптеров:

public class AdaptableBean implements Serializable {
   private PortletAdapter adapter;

   public PortletAdapter getAdapter() {
      return adapter;
   }

   public void setPortletAdapter (PortletAdapter adapter) {
      this.adapter = adapter;
   }
}

фактически мы реализовали механизм полноценной работы JSF framework внутри портальной среды. осталось только проиллюстрировать его конфигурацию. итак реализуем простой класс JSF managed bean, который используется в портлете:

public class TestBean extends AdaptableBean {
}

и сконфигурируем портлет, для использования этим классом GenericPortletFacesAdapter адаптера:

<faces-config>
   <managed-bean>
      <managed-bean-name>portletAdapter</managed-bean-name>
      <managed-bean-class>GenericPortletFacesAdapter</managed-bean-class>
      <managed-bean-scope>application</managed-bean-scope>
   </managed-bean>
   <managed-bean>
      <managed-bean-name>testBean</managed-bean-name>
      <managed-bean-class>TestBean</managed-bean-class>
      <managed-bean-scope>request</managed-bean-scope>
      <managed-property>
         <property-name>adapter</property-name>
         <value>portletAdapter</value>
      </managed-property>
   </managed-bean>
</faces-config>

ссылки
Java Portlet API
JavaServer Faces API
статья на английском языке