Путь разработчика portlet'ов под Liferay, использующего Service Builder, полон боли подводных камней. Сегодня хочу осветить решение, пожалуй, наиболее часто возникающей ошибки при разработке портлетов с использованием этой технологии.
Service Builder - это инструмент для кодогенерации service'ного слоя, который, по мнению разработчиков, уменьшает время разработки, путем написания xml файла "service.xml" и запуска "build-service", который по описанным в файле сущностям создаст весь необходимый код для доступа, хранения и представления данных.
Те, кто решил использовать Service Builder'ы в своих портлетах или вынужден заниматься поддержкой такого кода, могут столкнуться с ошибкой несоответствия контекста при вызове service'ных методов.
Лицезреть её можно в выводе stack trace'а в log'ах.
ERROR [PortletBeanLocatorUtil:38] BeanLocator is null for servlet context XXX-portlet
ERROR [PortletServlet:97] javax.portlet.PortletException: com.liferay.portal.kernel.bean.BeanLocatorException: BeanLocator has not been set for servlet context XXX-portlet
at javax.portlet.PortletException: com.liferay.portal.kernel.bean.BeanLocatorException: BeanLocator has not been set for servlet context XXX-portlet
at com.liferay.portal.kernel.bean.PortletBeanLocatorUtil.locate(PortletBeanLocatorUtil.java:46)
BeanLocator is null... - признак того, что причина вашего недуга лежит в Service Builder'ах. Имея ошибку, можно приступить к её предотвращению.
Стоит ответить на вопрос: "К сервисному слою какого приложения мы пытаемся обратиться?". Если вы не знаете ответа на этот вопрос, то спусайтесь вниз по стеку вызовов до тех пор пока не встретите класс следующего типа
ru.myCompany.somePackage.service.SomethingLocalServiceUtil.method(SomethingLocalServiceUtil.java:99)
Определите к service'у какого портлета относится этот класс. Далее имеем одну из трех (пока я открыл только 3 случая проявления ошибки) ситуаций:
1) Мы пытаемся вызвать метод портлета с контекстом XXX-portlet, а он развернут с отличным от этого контекстом. Посмотрите файл WEB-INF/liferay-plugin-package.properties, если контекст отличается от имя-портлета-portlet, то там должно быть указано с каким контекстом должен быть задеплоен портлет(строка recomended-deployment-context=Something).
Важно: Чтобы портлет задеплоился с рекомендуемым контекстом, pluginName в pom.xml, если он имеется, должен иметь такое же значение. Поправьте контекст вызываемого портлета и переразверните его.
2)Мы пытаемся вызвать метод портлета, но используем неверный контекст. Проверьте используемый контекст в файле WEB-INF/liferay-plugin-package.properties. Он указан в свойстве required-deployment-contexts. Исправьте проблемное значение на правильное.
3) При сборке портлета, имеем jar-ник с другой версией Service Builder'а. Необходимо открыть зависимости портлета, найти нужный вам jar файл и посмотреть содержимое файла something.service.ClpSerializer. В методе getServletContextName() полю _servletContextName присваивается, используемый контекст, если он отличается от развернутого, то необходимо пересобрать service вызываемого портлета, если сборка вызываемого портлета затруднительна, то можно решить проблему, подложив в локальный репозиторий jar-ник с сервисами из развернутого вызываемого портлета с сервера приложений. Он находится в директории usingPortlet.war/WEB-INF/lib/usingPortlet-portlet-service.jar.
Надеюсь, что я помогу кому-нибудь решить проблему "BeanLocator is null for servlet context...". Удачной вам разработки!