Понеділок, 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
статья на английском языке

Liferay + JSF + ICEfaces

копия моей статьи на Liveinternet.ru от 2 октября 2007

интеграцию портала Liferay и
JavaServer Faces
компонентов ICEfaces можно считать условно завершенной. безусловно, это только начало интеграции и впереди еще долгий путь полный устранения дефектов, реализации улучшений и всего прочего, чем богата наша програмисткая работа. но первый шаг сделан, анонсированная в начале лета интеграция AJAX JSF framework с Liferay порталом состоялась. сегодня я протестировал портлет, использующий ICEfaces components весрии 1.7-DR1 в контейнере Liferay Portal версии 4.3.2. успешно работает отрисовка, навигация и взаимодействие компонентов. уверен, что валидация также работает.


Liferay 4.3.0 & ICEfaces 1.6.0
именно начиная с этих версий была анонсирована интеграция и появился первый пример. при попытке запуска стандартноего (взятого с сайта Liferay) примера, начались проблемы:
  1. ошибки JavaScript, вызванные отсутсвием инициализированного объекта window.logger.


  2. Workaround: поместить на страницы портлета код, инициализирующий данный объект, в случае если объект отсуствует:

    <script type="text/javascript">
    if (window.logger == null) {
    window.logger = new ICE.Log.Logger(['window']);
    }
    </script>


  3. после решения проблемы с логгером, асинхронные обращения к серверу, посылаемые ICEface, завершаются ошибкой JavaScript.


  4. Solution: на странице портала должен быть... только один портлет с компонентами ICEfaces. тогда все работает нормально. если поместить туда другие портлеты, возникают ошибки. на форуме ICEfaces я обнаружил, что это проблемы интеграции, связанные непосредственно с Liferay Portal. и действительно, проблема была решена в версии 4.3.1


Liferay Portal 4.3.1 & ICEfaces 1.6.0
после решения проблемы взаимодействия портлетов, содержащих ICEfaces компоненты, и других портлетов в Liferay Portal, разработке ICEfaces портлетов мешала только одна проблема: ошибка, при обработке правил навигации внутри ICEfaces портлета. причной обшибки являлось предопределенное обновление контейнера <body id="body"> после выполнения правила навигации JSF. такое поведение недопустимо внутри порталов, поскольку портлет является лишь частью HTML страницы. решением проблемы могла стать замена стандартного ICEfaces ViewHandler, на разработанный специально для исправления этой ошибки.
ссылки
статья на английском
форум компонентов ICEfaces
матрица JSF компонентов

Liferay 4.2.2 themes handling bug

копия моей статьи на Liveinternet.ru от 11 июля 2007
Ошибка обработки идентификаторов темы в Liferay Portal 4.2.2
для версии 4.2.2 Liferay Portal зафиксирована не корректная работа с идентификаторами портальных тем, развернутых как внешние WAR-приложения с помощью механизма автоматического развертывания компонентов портала (тем, шаблонов расположения и портлетов).


проблема
ошибка заключается в возникновении java.lang.NullPointerException (далее NPE) при обращении к настройкам вида и поведения созданного комьюнити/группы/пользователя при установке темы, развернутой как внешнее WAR-приложение (далее Тема), в качестве темы используемой по умолчанию.
после исследования проблемы, определено, что источником ошибки является не корректная работа с идентификатором темы.
причины возникновения
для всех компонентов, развернутых с использованием механизма авто-развертывания, Liferay Portal генерирует сложные идентификаторы по шаблону <theme-id>_WAR_<web.xml_display_name>. к примеру если тема имеет theme-id=foo, определенный в liferay-look-and-feel.xml, и display-name=bar, определенный в web.xml, то конечный идентификатор Темы в Liferay Portal будет foo_WAR_bar.
стандартная документация описывает изменение параметров, установленных по-умолчанию, с использованием идентификатора темы в том виде, в котором он определен в liferay-look-and-feel.xml (для примера приведенного выше -- foo). однако, API портала не обрабатывает идентификатор для Тем и использует его значение из portal(-ext).properties без изменений. таким образом, происходит попытка обращения к несуществующей Теме и возникает NPE.

решение
для решения данной проблемы необходимо определять идентификатор темы в portal(-ext).properties с учетом его автоматической модификации внутри портала. иными словами, значение параметра default.theme.id должно быть не theme_id, а <theme_id_WAR_<web.xml_display_name>. дла описанного выше примера соотвественно -- не default.theme.id=foo, а default.theme.id=foo_WAR_bar.
для использования произвольной Темы, в качестве темы по-умолчанию, будет полезна следующая инструкция:
1. развернуть Тему следуя стандартному процессу.
2. авторизоваться в Liferay Portal как пользователь с правами администратора и установить Тему в качестве темы любого доступного компьюнити.
3. открыть базу данных портала в любом SQL-клиенте.
4. выполнить select themeId from layoutset where groupId = (select groupId from group_ where name = )>, где community_name - имя комьюнити, где использована Тема.
5. использовать выбранный идентификатор, как значение параметра default.theme.id

область распространения
проблема выявлена для версии портала 4.2.2 и не тестировалась на других версиях. не исключено исправление ошибки в старших и отсутствие в младших версиях.
для предустановленных тем портала стандартный механизм работает корректно.

ссылки
комментарий на форуме Liferay Portal(англ.)

once upon time....

что ж, попробуем поселится здесь :) всем доброго времени суток.