четверг, 30 августа 2012 г.

Решение проблемы с кодировкой в Spring компонентах c использованием @RequestMapping

Добавляем Spring Intercepter для явного указания кодировки в запросе HttpServletRequest:
package ru.myapp.service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

public class ServiceCharacterEncodingInterceptor extends HandlerInterceptorAdapter {

    private String characterEncoding = "UTF-8";

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        request.setCharacterEncoding(characterEncoding);
        return super.preHandle(request, response, handler);
    }

    public void setCharacterEncoding(String characterEncoding) {
        this.characterEncoding = characterEncoding;
    }
}
Прописываем в конфигурации Spring:
    <mvc:interceptors>
        <bean class="ru.myapp.service.ServiceCharacterEncodingInterceptor"/>
    </mvc:interceptors>

Взято отсюда

вторник, 24 июля 2012 г.

Java vs OpenVZ + CentOS kermel update 2.6.32-042

У нас имеется небольшой веб-сервис бегающий на Apache Tomcat 6.0.32, размещенный на VPS. На этом VPS также размещены клиентские сайты под управлением NetCAT CMS - собственно сервис работает для передачи данных из БД этой CMS в мобильное приложение

В прошлую среду (в середину моего отпуска!) VPS ушел в отказ. Точнее сказать начал очень сильно тормозить. Наш админ отправил VPS в restart. После перезапуска работа клиентских сайтов нормализовалась, а вот Tomcat перестал запускаться.

Симптомы:
1. Запуск - в логах может начать что-то выдаваться, а может и ничего не выдаваться
2. Процесс java висит нагрузки на CPU не дает (периодиески по 0.3% скачет)
3. Удалили все приложения запустили чистый tomcat - запустился, работает и отвечает по http.
4. Делаем деплой нашего небольшого приложения. В логах выдается
Jul 18, 2012 4:17:55 PM org.apache.catalina.startup.HostConfig deployDescriptor                                                    
INFO: Deploying configuration descriptor WebApplication.xml
И всё
5. Чистый Tomcat не всегда запускается, иногда крешится:
#                                            
# A fatal error has been detected by the Java Runtime Environment:
#                                            
#  Internal Error (synchronizer.cpp:1429), pid=1083, tid=3040942992
#  guarantee(mid->header()->is_neutral()) failed: invariant              
#
# JRE version: 7.0_05-b06
# Java VM: Java HotSpot(TM) Client VM (23.1-b03 mixed mode, sharing linux-x86 )
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#                                        
# An error report file with more information is saved as:
# /srv/java/tomcat6/conf/hs_err_pid1083.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.sun.com/bugreport/crash.jsp
#
6. Иногда в Tomcat выдается:
Exception in thread "Reference Handler" java.lang.IllegalMonitorStateException
        at java.lang.Object.notifyAll(Native Method)
        at java.lang.ref.ReferenceQueue.enqueue(ReferenceQueue.java:51)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:129) 


Мои коллеги в моё отсутствие пробовали переустановить JRE, поставить свежую 1.7, ставили новый tomcat. Картина та же. Обращались в техподдержку - явно были произведены какие-то изменения в конфигурации VPS - безрезультатно.

В понедельник перенесли сервис на наш сервер и на VPS клиента включили HTTP-прокси на наш сервак.

Итог

Первое что нашел http://serverfault.com/questions/389152/java-hangin-and-crashing-on-centos-6
Оттуда сюда http://forum.proxmox.com/threads/6998-Best-strategy-to-handle-strange-JVM-errors-inside-VPS

Проверили систему установленную на VPS - Хостинг-провайдер провел обновление ядра - сборка свежая от 10 мая (2.6.32-042stab055.10 #1 SMP Thu May 10 15:38:32 MSD 2012 i686 i686 i386 GNU/Linux)!

По какой-то причине с последней версией ядра в OpenVZ Container-е с 1 CPU Java не работает.

Из предложенных решений в обсуждении по последней ссылке:
1. Увеличить кол-во CPU как минимум до 2-х
2. Откатить обновление.

Мы всю информацию передали хостинг-провайдеру. Благо дело, техподдержка отреагировала очень адекватно - пошла навстречу и увеличила число CPU до 2-х. Всё заработало.

четверг, 28 июня 2012 г.

Liferay вложенные портлеты

У меня есть Vaadin портлет с интерфейсом управления контентом (кнопки вызывающие различные окна редактирования и т.д.) и мне необходимо его подключить к front-end портлету отображения контента.

Как вложить портлет в портлет? В стандартном лайфреевском портлете "Nested portlets"  используется RuntimePortletUtil.processTemplate. В этом же сервисе есть методы вызова портлета processPortlet. На сайте liferay.com статьи описывающий способ программного вызова этих методов - нет.

Метод тыка результата не приносил.

Просмотрел исходники имплементации RuntimePortletImpl - используется PortalUtil.renderPortlet. По запросу гугл выдал исчерпывающий рабочий пример:
http://www.devatwork.nl/2011/07/liferay-embedding-portlets-in-your-portlet/

Единственное не понял зачем при рендере любого портлета выполняется получение PortletBag соответствующее портлету JOURNAL. После экспериментов выяснил, что  для PortalUtil.renderPortlet первым параметром надо передать портальный ServletContext, иначе будет выдаваться "File &quot;/html/portal/render_portlet.jsp&quot; not found"

Но насколько я понимаю servletContext портала из любого портлета можно получить из PortalUtil.getOriginalServletContext(...).getServletContext(). Мой код практически не отличается от приведенного Bert Willems


    public static String renderPortlet(final PortletRequest request, final PortletResponse response, final String portletId, final String queryString) {
        String result = "Error occured while running portlet";
        try {
            // Get servlet request / response
            HttpServletRequest servletRequest = PortalUtil.getHttpServletRequest(request);
            HttpServletResponse servletResponse = PortalUtil.getHttpServletResponse(response);
            HttpServletRequest portalServletRequest = PortalUtil.getOriginalServletRequest(servletRequest);

            // Get theme display
            final ThemeDisplay themeDisplay = (ThemeDisplay) servletRequest.getAttribute(WebKeys.THEME_DISPLAY);
            // Backup current state
            PortletDisplay portletDisplay = themeDisplay.getPortletDisplay();
            PortletDisplay portletDisplayClone = new PortletDisplay();
            portletDisplay.copyTo(portletDisplayClone);
            final Map requestAttributeBackup = new HashMap();
            for (final String key : Collections.list((Enumeration) servletRequest.getAttributeNames())) {
                requestAttributeBackup.put(key, servletRequest.getAttribute(key));
            }
            // Render the portlet as a runtime portlet
            try {
                com.liferay.portal.model.Portlet portlet = PortletLocalServiceUtil.getPortletById(PortalUtil.getCompanyId(request), portletId);
                servletRequest.setAttribute(WebKeys.RENDER_PORTLET_RESOURCE, Boolean.TRUE);
                result = PortalUtil.renderPortlet(portalServletRequest.getServletContext(), servletRequest, servletResponse, portlet, queryString, false);
            } finally {
                // Restore the state
                portletDisplay.copyFrom(portletDisplayClone);
                portletDisplayClone.recycle();
                for (final String key : Collections.list((Enumeration) servletRequest.getAttributeNames())) {
                    if (!requestAttributeBackup.containsKey(key)) {
                        servletRequest.removeAttribute(key);
                    }
                }
                for (final Map.Entry entry : requestAttributeBackup.entrySet()) {
                    servletRequest.setAttribute(entry.getKey(), entry.getValue());
                }
            }
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        }
        return result;
    }

четверг, 31 мая 2012 г.

С момента выхода Liferay CE 6.1 мы решили перейти со старенького монстра 5.2.3 на него.
Первое с чем столкнулся это StackOverflowError. Проявляется после установки приложений (портлетных/тем/hook) и обращения к ним. Страницы с подключенным плагином (портлетом или темой) не открываются, панель управления работает в штатном режиме.

При установке на продакшн сервер эта проблема тоже возникала, однако очистка папок temp и work с последующей перезагрузкой сервера и переустановкой приложения решало проблему.

Однако разработку на 6.1 вести было невозможно, поэтому работал я на Liferay Portal CE 6.0.5

Но сегодня пришлось заняться этой проблемой вплотную: нашему верстальщику требовалось подготовить тему оформления под Liferay 6.1. Вчера он инициировал проект с темой оформления и уперся в StackOverflowException - работать нет возможности

Никакие танцы с бубнами вокруг установленного на его машине Liferay 6.1 не помогали.
Погуглили, нашли обсуждения на форуме http://www.liferay.com/community/forums/-/message_boards/message/12134612 - народ решил свою проблему путем обновления Liferay IDE. Однако мы ею не пользуемся, и явно проблема не в среде а в самом портале. В этом обсуждении упомянули liferay-web.xml и что проблема заключается в том, что туда попадает InvokerFilter. Проверили так и есть.

Поискал в Liferay JIRA и нашел http://issues.liferay.com/browse/LPS-23426. Оказывается использование liferay-web.xml можно вообще выключить в liferay-plugin-package.properties директивой liferay-web-xml-enabled=false

Всё заработало.