Почему мой первый портлет «Hello World» LifeRay стал «временно недоступным»?
Я абсолютно новый в разработке портлетов LifeRay, и у меня есть проблема с созданием тестового портлета Hello World в проекте, над которым я работаю.
В этом проекте используется Structs 2 для отправки.
Поэтому я выполнил следующие операции:
1) Я создаю класс TestPortlet1Action в этой папке: /Web/src/main/java/mypackage/actions/ где я определяю метод startTestPortlet1(), который является стартовым методом моего пролета (я думаю, что это точка входа портлета ):
где KMAction доказывал мне этот интерфейс Action:
Таким образом, возвращаемый индекс SUCCESS возвращает строку успеха.
2) Затем я создал представление JSP для этого портлета с именем testPortlet1.jsp (в папку: /Web/src/main/webapp/testPortlet1/testPortlet1.jsp):
3) Итак, теперь я знаю, что мне нужно обновить файл struts.xml (который находится в папке Web/main/resources), который содержит что-то вроде:
Как вы можете видеть, я включил конфигурационный файл struts, связанный с моим мировым портлетом hello:
4) Итак, теперь я настроил файл TestPortlet1-struts.xml следующим образом:
Поэтому у меня есть что-то вроде этого (для другого рабочего сервлета):
Таким образом, первый раздел определил сервлет, а второй раздел определил шаблон URL, который был обработан этим сервлетом. Это правда или я чего-то не хватает?
Итак, что мне сделать, чтобы настроить мой предыдущий сервлет «Hello World test»? Это нормально?
Это нормально или я что-то упускаю?
6) Затем мне нужно настроить файл portlet.xml в папку **\Web\src\main\webapp\WEB-INF **, добавив:
где startTestPortlet1 представляет метод startTestPortlet1(), определенный в моем классе действий.
7) Затем я добавляю этот элемент в файл конфигурации liferay-portlet.xml:
8) Наконец, я настроил файл liferay-display.cml, чтобы установить категории моего портлета, примерно так:
Проблема в том, что когда я пытаюсь добавить портлет на главную страницу портала, я получаю этот параметр вместо ожидаемого результата Hello World:
Налоговый кодекс, общая часть, Статья 63. Налоговая декларация (расчет)
3. Налоговая декларация (расчет) представляется в налоговый орган по месту постановки на учет плательщика (иного обязанного лица) в порядке и сроки, установленные настоящим Кодексом или иными актами налогового законодательства. 4. Налоговая декларация (расчет) представляется в налоговый орган по установленной форме на бумажном носителе или по установленным форматам в виде электронного документа, если иное не установлено частью второй пункта 8 настоящей статьи. Документы, которые в соответствии с настоящим Кодексом или иными актами налогового законодательства должны прилагаться к налоговой декларации (расчету), представляются на бумажном носителе.
Плательщики, среднесписочная численность работников которых за предшествующий календарный год превышает 50 человек, а также плательщики налога на добавленную стоимость представляют в налоговый орган налоговые декларации (расчеты) по установленным форматам в виде электронного документа. Среднесписочная численность работников определяется в порядке, установленном Национальным статистическим комитетом Республики Беларусь
Подключение к системе электронного декларирования всех желающих Плательщиков начинается с 01.04.2009. (с) Министерство по налогам и сборам
В Инспекции по налогам и сборам предложили добровольно перейти на электронное декларирование. Сказали, мол, это намного удобнее, чем ходить в налоговую и носить вручную заполненные декларации. Узнав, что стоимость данной услуги составляет 270 тыс.руб в год я отказался, пояснив, что мне абсолютно несложно 1 раз в 3 месяца (УСН) посетить инспекцию и собственноручно отдать декларацию; а на сэкономленные деньги лучше буду каждый месяц покупать дополнительные фрукты-овощи для недавно родившегося ребенка. Озарив меня ехидной улыбкой, инспектор пояснила, что в нашей стране нет такого понятия как «принудительно», но мне необходимо понимать, что их обязали в течение 2010 года перевести всех (!) налогоплательщиков на электронное декларирование и будет формироваться список несогласных с политикой партии со всеми вытекающими. Я отношусь индифферентно к таким, если можно так выразиться, угрозам и не боюсь проверок со стороны налоговой, но мне излишнее внимание со стороны проверяющих, по понятным причинам, незачем. Кто-либо сталкивался с подобным, либо кто-что слышал или работает с электронным декларированием? Спасибо.
Портлет – это компонент веб-страницы. Портлет имеет настройки от которых зависит его функциональность и внешний вид.
Пример: портлет Задачи:
Веб-страницы могут формироваться из произвольного набора портлетов, эту задачу может выполнять администратор или пользователь наделенный соответствующими правами.
Администратор может видеть список всех доступных в системе портлетов на странице Портлеты по адресу /ContentArea/Portlets, там же задаются настройки портлетов по умолчанию.
Настройки портлетов
Общий вид и содержимое страниц задается администратором, при этом пользователи могут настраивать интерфейс и менять логику работы. Инфраструктура портала позволяет это сделать за счет персонализации.
Персонализация позволяется сохранять настройки страниц и портлетов в долговременное хранилище с возможностью разделения индивидуальных данных.
Область персонализации – это круг пользователей, на которых распространяется изменения персонализации на данной странице (в портлете). Каждый порлет на странице может находится в одной из двух областей: Shared, User. Изменения в области Shared применяются ко всем пользователям, в области User – только к текущему пользователю.
Видимость портлета определяет является ли данный портлет видимым для отдельного пользователя или для всех пользователей. Видимость портлета определяется каким образом он добавлен на страницу, если добавлен как Shared, то портлет является общим, если как User, то – индивидуально настраиваемым.
Область свойства позволяет установить контроль над тем, какие свойства персонализации будут настраиваться всеми пользователями, а какие только администратором в зависимости от того в какой области находится портлет.
Таблица соответствия областей персонализации портлетов и свойств.
Портлет/Свойство
Shared
User
Shared
Значение может менять только администратор или пользователь наделенный соответствующими правами.
Отдельные пользователи могут менять персональные свойства. Поскольку свойства Zone, Order, Collapsed являются User Scope пользователи могут переносить портлеты, сворачивать и разворачивать (сворачивание может быть запрещено отдельно).
User
Отдельный пользователь может менять настройки User и Shared Scope, поскольку портлет является недоступным для других пользователей.
То же, что и для Shared.
Пример использования персонализации в главных страницах
Главные страницы работают следующим образом, администратор создает набор главных страниц, все портлеты добавляются в Shared Scope, из-за чего они выгладят для всех пользователей одинаково. Отдельно взятый пользователь может персонализировать страницы, менять расположение портлетов, настройки отображения, т.е. менять настройки User Scope, может добавлять другие портлеты, при этом персональные изменения не увидят другие пользователи. Таким образом, администратор может установить базовый набор портелтов на страницах, который пользователи могут настроить под себя, при этом остается контроль за общими портлетами, например, один из портлетов можно удалить и он исчезнет у всех пользователей
Базовые настройки портлетов
Все портлеты имеют базовый набор настроек, дополнительно к базовым настройкам каждый портлет может иметь дополнительные настройки.
Настройки доступные через диалог настроек.
Свойство
Категория
Описание
Текст в заголовке портлета. Если значение не введено, то будет показано значение по умолчанию для данного типа портлета.
Определяет стиль отображения границ портлета. Возможны четыре значения:
Обратите внимание на то, что кнопки настройки портлета в режиме просмотра страницы будут видны только при первом выбранном значении, в других случаях диалог настройки портлета можно будет вызвать только из режима настройки страницы.
Запретить или разрешить пользователю сворачивать портлет.
Задает гипертекстовую ссылку в заголовке портлета, если значение не введено то будет использоваться значение по умолчанию для данного типа портлета.
Задает адрес изображения в заголовке портлета. Если значение не введено, то будет использоваться значение по умолчанию для данного типа портлета.
Включить асинхронную загрузку
Если выбрано значение Да, то содержимое портлета загружается после загрузки содержимого страницы по средствам отдельного http-запроса. Включение этой опции может ускорить загрузку страницы, на которой размещено много портлетов.
Показать кнопку ручного обновления
Если включено, то в заголовке портлета появляется дополнительная кнопка, которая перезагружает содержимое портлета без перезагрузки всей страницы.
Значения свойства устанавливается в true, когда пользователь закрывает портлет нажатием кнопки (Х).
Свойство хранит идентификатор зоны, в которой располагается портлет. Значение меняется, когда пользователь перетаскивает портлет методом drag’n’drop.
Порядок расположения портлета в зоне, представляет из себя число. Значение меняется, когда пользователь перетаскивает портлет методом drag’n’drop.
Портлет свернут/развернут, значение меняется, когда пользователь сворачивает или разворачивает портлет.
Настройки портлетов по умолчанию
Администратор может установить значения настроек портлета по умолчанию, эти значения будут использованы при добавлении нового портлета на страницу. Например, в настройках по умолчанию для какого-то портлета можно включить асинхронную загрузку.
Как сделать свой портлет
После этого в Ваш проект будет добавлен файл, включающий в себя представление и сам класс, реализующий логику работы портлета. Уже сейчас Вы можете собрать свой проект, и добавить Ваш модуль в папку Modules установленной системы, для того чтобы увидеть Ваш портлет. Далее представлено более детальное описание каждого метода и свойства класса портлета.
Минимальный набор методов, который нужно определить, это:
Пример простого портлета, который показывает форму для запуска бизнес-процессов:
Пример персонализируемого свойства:
В приведенном примере указана область Shared, кроме того, указаны категория и экранное название свойства, которые используется при генерации формы настроек.
Для редактирования свойств используется всплывающий диалог, есть два варианта встраивания дополнительный свойств в эту форму. Первый способ, он же самый простой, это автоматическая генерация формы. Все свойства помеченные атрибутом PersonalizationAttribute автоматически отображаются в этой форме, сохранение и отображение значений выполняется автоматически. Пример свойство Сортировать по для портлета Задачи:
Диалог настройки выгладит так:
Второй способ, это добавить в диалог свою форму, для этого следует переопределить метод:
Форма настроек выглядит так:
Кроме того, можно менять персонализацию портлетов программно, для этого следует использовать PortletManager API.
Дополнительные возможности для разработки портлетов
Несколько одинаковых портлетов на странице – если свойство портлета AllowMultipleInstance равно false, то пользователь не сможет вывести на страницу второй такой портлет.
Привет хабралюди! Сегодня я хочу рассказать об одной интересной технологии, с которой познакомился совсем недавно — это технология портлетов. Хотя на хабре уже есть пара упоминаний о портлетах, но там ничего внятного я не нашел. Поэтому решил написать свою статью, где хочу показать на практике как программировать портлеты. При этом попутно вставляя какие-то теоретические сведения. А принимая во внимание, то, что документации на русском крайне мало, то рассказать об этом хочется вдвойне 🙂
Чтобы быть немного в курсе дела, нам потребуется несколько определений:
Портал в простейшем его рассмотрении — это программная платформа, содержащая портлет-контейнер, который в свою очередь поддерживает Portlet API и позволяет нам запускать портлеты. Так же помомо этого, портал обычно предоставляет средства для управления группами и пользователями, а так же широкие возможности их кастомизации.
Портлет — это отдельное небольшое веб-приложение, которое выполняется на портале, портал в свою очередь агрегирует один или несколько портлетов на отдельной веб странице, которые обычно настраиваются для для отдельных пользователей и групп портала. В итоге мы получаем обычную веб-страницу, наполненную несколькими веб-приложениями. И еще, портлеты имеют много аналогий с сервлетами, я думаю, что многие, кто вообще в курсе дела, это заметят. Лишний раз проводить аналогии и останавливаться на этом не буду.
В этой статье буду описывать разработку портлетов в рамках спецификации JSR 168, но не буду рассказывать о выборе портала, о его установке, деплое портлетов и т.п. Сразу скажу, что разрабатывал приложение на IBM Web Sphere Portal 6. Если кто-то расскажет об особенностях разработки и деплоя на других порталах типа Jboss Portal или еще какого, то буду очень признателен 🙂
Итак приступим к разработке, а разрабатывать мы будем… на самом деле долго думал, что лучше привести как пример, хотел, чтобы пример не был чем-то тривиальным типа Hello World, хотел чтобы портлету нашлось практическое применение и выбор мой пал на портлет для агрегации RSS. А что? Актульно!
Итак, наше мини ТЗ: Необходимо написать портлет, который будет собирать rss с различных каналов и отображать его. При этом каждому пользователю портала мы предоставим возможность самому настраивать портлет под свои нужды, т.е. создавать и удалять отображаемые каналы, а так же устанавливать количество новостей с каждого канала и частоту их обновления.
Сперва давайте создадим список классов, библиотек и jsp-шек, которые потребуются нам для нашего приложения:
1) HabraRssPortlet — собственно сам класс портлета. 2) RssUtil — класс для работы с RSS. 3) rssutils — библиотека (конечно можно и самому парсер написать, но это займет время и наверняка сразу хорошо не получится — проверено опытом) 3) HabraRssPortletView.jsp — для отображения Rss. 4) HabraRssPortletEdit.jsp — для конфигурирования портлета пользователем. 5) HabraRssPortletHelp.jsp — где будет что-то типа «About» 6) portlet.xml и web.xml — ну я думаю понятно для чего…
Хочу начать с дескриптора развертывания portlet.xml дабы объяснить некоторые моменты, которые понадобятся нам позже, дескриптор web.xml приводит тут не буду, там все до боли просто. Если кому-то интересно, то его можно всегда взглянуть в аттаче.
portlet.xml
preference > name > updateFrequency name >
value > 10 value > preference > preference > name > itemQuantity name > value > 3 value > preference > preference > name > rssLinks name > value > www.vz.ru/rss.xml value > preference >
Ниже приведен основной класс нашего приложения — HabraRssPortlet, который является потомком класса GenericPortlet, а он в свою очередь является абстрактным классом и обеспечивает дефолтную имплементацию интерфейса Portlet. Все тот же GenericPortlet обеспечивает поддержку несколько режимов: VIEW, EDIT, HELP, еще иногда бывают кастомные режимы типа CONFIG, но это уже зависит от конкретной реализации портала. Для конечного пользователя все три режима чаще всего представлены различной отображаемой информацией и доступным функционалом в том или ином режиме. Переход между режимами осуществляется либо через меню портлета, либо через кнопки/ссылки. Обычно у любого класса портлета перегружаются несколько методов, чаще всего это методы doView, doEdit и doHelp, каждый из которых и отвечает за соответствующий режим.
public class HabraRssPortlet extends GenericPortlet <
public static final String JSP_FOLDER = «/_HabraRssPortlet/jsp/» ; public static final String VIEW_JSP = «HabraRssPortletView» ; public static final String EDIT_JSP = «HabraRssPortletEdit» ; public static final String HELP_JSP = «HabraRssPortletHelp» ; public static final String CONFIG_JSP = «HabraRssPortletConfig» ; public static final String RSS_LINKS = «rssLinks» ; public static final String UPDATE_FREQUENCY = «updateFrequency» ; public static final String ITEM_QUANTITY = «itemQuantity» ; public static final String NEW_RSS_LINK = «newRssLink» ; public static final String ADD_NEW_RSS_LINK = «addNewRssLink» ; public static final String DELETE_RSS_LINK = «deleteRssLink» ; public static final String RSS_LINKS_LIST = «rssLinksList» ; public static final String FREQUENCY = «frequency» ; public static final String QUANTITY = «quantity» ; public static final String SET_FREQUENCY = «setFrequency» ; public static final String SET_QUANTITY = «setQuantity» ; public static final String ERROR_MESSAGE = «errorMessage» ; public static final String UPDATE_FEEDS = «updateFeeds» ; public static final String EDIT_BACK = «editBack» ;
private static HashMap rssMap = new HashMap(); private static HashMap updateTimeMap = new HashMap();
if (!rssMap.containsKey(request.getUserPrincipal().getName())) < rssMap.put(request.getUserPrincipal().getName(), new ArrayList ()); updateTimeMap.put(request.getUserPrincipal().getName(), new Long(0));
private static boolean isFound( String [] array, String s) < boolean found = false ; for ( int i = 0; i if (array[i].equals(s)) < found = true ; break ; > > return found; >
private static boolean isDigit( String s) < char [] array = s.toCharArray(); for ( int i = 0; i if (!Character.isDigit(array[i])) < return false ; > > return true ; >
private static Long getLastUpdateTime(PortletRequest request) < return (Long)updateTimeMap. get (request.getUserPrincipal().getName()); >
private static void setLastUpdateTime(PortletRequest request) < updateTimeMap.put(request.getUserPrincipal().getName(), new Long(System.currentTimeMillis())); >
С набором констант все ясно, далее мы создаем два статических объекта типа HashMap, — ключом ко всем значениям обеих карт служит имя конкретного пользователя портала, значением либо объект типа ArrayList, содержащий Rss-контент, либо объект типа Long, содержащий время последнего обновлнения каналов. Так как время жизни статической переменной портлета равно времени жизни самого портлета, то я решил хранить данние именно в них, а не персистить их куда-либо, тем более я вижу мало смысла в сохранении rss’a в какую-нибудь базу данных.
Далее начинается то, о чем мы говорили чуть выше, а именно переопределение методов класса GenericPortlet:
Метод doView: от объекта запроса получаем объект типа PortletPreferences, из которого в свою очередь можем вытащить нужное нам значение или же наоборот внести в него свое значение. Потом проверяем нашу первую карту на наличие в ней ключа с именем активного пользователя, если такого ключа нет, то заносим в обе карты пары ключ-значение, в одном случае значение — это объект ArrayList, в другом — объект Long, содержащий время последнего обновления. Далее проверяем, нужен ли нам апдейт новостей. Напомню, что для всех пользователей по умолчанию установлен один канал, одно и тоже количество новостей и время их обновления (см. portlet.xml) Итак, если пользователь зашел первый раз на страничку с нашим портлетом, то для него сразу же выполнится обновление канала, полученный rss-контент заносим в наш ArrayList, который мы получили из карты с rss-контентом по имени пользователя (ключ). Далее все просто — в запрос кладем все тот же ArrayList, вызываем PortletRequestDispatcher и перенаправляемся на jsp-страничку HabraRssPortletView, котрая и будет отображать наш rss-контент:
HabraRssPortletView.jsp
На страничке из запроса получаем наш объект ArrayList, итерируем его с помощью JSTL и выводим наш Rss-контент.
Метод doEdit: тут ничего особенного нет, так же вызываем PortletRequestDispatcher и переходим на jsp-страничку HabraRssPortletEdit. Вся логика зашита именно в ней, хоть это и не есть хорошо. Мы, конечно же, без проблем межем все это исправить 🙂 Что же происходит на этой страничке? В скриплете с помощью все того же объекта PortletPreferences мы получаем необходимые нам значения, а именно:
а)Список rss каналов. б)Частоту обновления. в)Количество новостей из каждого канала.
Далее рисуем необходимые нам поля, списки и кнопочки, вставляя в них значения, полученные чуть выше, и которые мы можем потом свободно редактировать, т.е. добавлять rss-каналы, удалять их, изменять значения частоты обновления и т.д.
Метод doHelp: тут ничего интересного совсем уже нет, вызываем все тот же PortletRequestDispatcher и перенаправляемся на страничку HabraRssPortletHelp, там пишем что-угодно, хотя обычно принято писать что-то типа справочных сведений.
Метод processAction: — сердце нашего портлета, именно тут идет вся обработка взаимодействия с пользователем, в нашем случае это все сабмиты с форм, которые располежены на страничке HabraRssPortletEdit. Прошу заметить, что в этом методе используются объекты типа ActionRequest и ActionResponse, в отличии от RenderRequest и RenderResponse, которые ответственны за рендеринг портлета, эти обеъкты ответственны за взаимодействие с пользователем. Обработка заключается в простейшей валидации входных параметров, изменении и создании значений preferences. Замечу, что валидаюцию preferences можно вынести в отдельный класс, который должен имплементиться от PreferencesValidator, при этом надо не забыть прописать этот класс в дескриптор portlet.xml. Мы же сделали немного попроще, если входящие параметры нас не устраивают, то мы устанавливаем объекту типа ActionResponse параметр рендеринга, содержащий сообщение об ошибке, это сообщение будет выводиться на той же старничке.
Да, еще очень важно то, что изменять значения preferences можно только внутри метода processAction. Самое главное, что стоит не забывать, это после всех измененийв вызвать метод store() у объекта типа PortletPreferences, иначе изменения не сохранятся. Прощу заметить, что портал хранит preferences отдельно для каждого пользователя, поэтому если один пользователь добавит в список своих каналов еще несколько ссылок, то у других пользователей все так же и останется одна ссылка, то же касается и остальных значений preferences, они индивидуальны для каждого пользователя. Правда очень удобно? 🙂
Теперь давайте немного рассмотрим класс, отвечающий за RSS. Я уже упоминал выше, что я воспользовался готовой библиотекой, скачать можно тут, может она не самая лучшая, но что есть, то есть 🙂
RssUtil.java
public class RssUtil <
Класс состоит из двух статических методов, один из которых собственно получает Rss-контент из имеющихся каналов, а другой проверяет не пора ли обновиться. Ничего сложного 🙂
Ну вот вроде бы и все. Все что тут было сказано, это мое личное ИМХО. Если у кого-то возникли вопросы — задавайте в камментах 🙂 Весь код и war-архив можно взять в аттаче 🙂