Liferay7 BPM门户开发之37: Liferay7下的OSGi Hook集成开发
hook开发是Liferay客制扩展的一种方式,比插件灵活,即可以扩展liferay门户,也能对原有特性进行更改,Liferay有许多内置的服务,比如用hook甚至可以覆盖Liferay服务。
可作为系统服务挂钩(Liferay Service Hook),还有其他类型的hook...
Liferay6.2 时的hook开发比较有限,而在Liferay7则大为不同,OSGi services的彻底改进至Liferay的底层模型框架,使得Liferay可以支持更多的定制扩展!
OSGi plugins可以快速部署到Liferay,就和其他类型的插件一样。
名词:
FQPN: a fully qualified portlet name
正在不断整理完善中...
PollerProcessor
com.liferay.portal.kernel.poller.PollerProcessor
集成点:"javax.portlet.name" : a FQPN
用途: 轮训消息机制,用于类似Ajax在页面显示信息
例子:
JS:
AUI() .use('aui-base','aui-delayed-task', 'liferay-poller', function(A) { Liferay.namespace('BladePoller'); Liferay.BladePoller.Manager = { init: function() { var instance = this; instance._portletId = A.one("#pollerPortletId").val(); instance._bladePollerContainer = A.one('#bladePollerContainer'); console.log("Init: portletId:"+instance._portletId+ ", containerId:"+instance._bladePollerContainer); instance._updateTask = new A.debounce(instance._updateMessage,30000, instance); instance._updateTask.delay(0); Liferay.Poller.addListener(instance._portletId,instance._onPollerUpdate, instance); Liferay.on( 'sessionExpired', function(event) { Liferay.Poller.removeListener(instance._portletId); instance._bladePollerContainer.hide(); } ); }, send: function(options, id) { console.log("Options:" + options + ", id:" + id); var instance = this; Liferay.Poller.submitRequest(instance._portletId, options, id); instance._updateTask(); }, _updateMessage: function() { console.log("Update Message"); var instance = this; instance.send( { status : 'OK' }); }, _onPollerUpdate : function(response, chunkId) { console.log("updating..."); var instance = this; instance._bladePollerContainer.text(response.content.message); instance._bladePollerContainer.show(); instance.send( { status : 'OK' } ); } }; A.augment(Liferay.BladePoller.Manager, A.Attribute, true); Liferay.publish( 'pollerPortletReady', { defaultFn: A.bind('init', Liferay.BladePoller.Manager), fireOnce: true } ); A.on( 'domready', function() { Liferay.fire('pollerPortletReady'); } ); } );
jsp:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> <%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet" %> <%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %><%@ taglib uri="http://liferay.com/tld/portlet" prefix="liferay-portlet" %><%@ taglib uri="http://liferay.com/tld/theme" prefix="liferay-theme" %><%@ taglib uri="http://liferay.com/tld/ui" prefix="liferay-ui" %> <liferay-theme:defineObjects /> <portlet:defineObjects /> <p> <b><liferay-ui:message key="blade_portlet_BladePollProcessorPortlet.title" /></b> </p> <input id="pollerPortletId" type="hidden" value="<%= portletDisplay.getId() %>" /> <p id="bladePollerContainer"></p>
Java:
import com.liferay.portal.kernel.portlet.bridges.mvc.MVCPortlet; import javax.portlet.Portlet; import org.osgi.service.component.annotations.Component; @Component( immediate = true, property = { "com.liferay.portlet.css-class-wrapper=portlet-pollprocessor-blade", "com.liferay.portlet.display-category=category.sample", "com.liferay.portlet.header-portlet-javascript=/js/main.js", "com.liferay.portlet.poller-processor-class=blade.portlet.BladePollProcessor", "com.liferay.portlet.private-request-attributes=false", "com.liferay.portlet.private-session-attributes=false", "com.liferay.portlet.remoteable=true", "com.liferay.portlet.render-weight=50", "javax.portlet.display-name=BLADE PollProcessor", "javax.portlet.expiration-cache=0", "javax.portlet.init-param.template-path=/", "javax.portlet.init-param.view-template=/view.jsp", "javax.portlet.portlet.info.keywords=blade,pollprocessor", "javax.portlet.portlet.info.short-title=BLADE PollProcessor", "javax.portlet.portlet.info.title=BLADE PollProcessor", "javax.portlet.resource-bundle=content.Language", "javax.portlet.security-role-ref=power-user,user" }, service = Portlet.class ) public class BladePollProcessorPortlet extends MVCPortlet { } import com.liferay.portal.kernel.json.JSONFactoryUtil; import com.liferay.portal.kernel.json.JSONObject; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.poller.BasePollerProcessor; import com.liferay.portal.kernel.poller.DefaultPollerResponse; import com.liferay.portal.kernel.poller.PollerProcessor; import com.liferay.portal.kernel.poller.PollerRequest; import com.liferay.portal.kernel.poller.PollerResponse; import java.util.Date; import org.osgi.service.component.annotations.Component; @Component( immediate = true, property = {"javax.portlet.name=blade_portlet_BladePollProcessorPortlet"}, service = PollerProcessor.class ) public class BladePollProcessor extends BasePollerProcessor { @Override protected PollerResponse doReceive(PollerRequest pollerRequest) throws Exception { if (_log.isDebugEnabled()) { _log.debug("Recevied the poller request" + pollerRequest); } JSONObject responseJSON = JSONFactoryUtil.createJSONObject(); PollerResponse pollerResponse = new DefaultPollerResponse(); responseJSON.put( "message", "Hello from BLADE Poller, time now is:" + new Date()); pollerResponse.setParameter("content", responseJSON); return pollerResponse; } @Override protected void doSend(PollerRequest pollerRequest) throws Exception { String status = getString(pollerRequest, "status"); if (_log.isInfoEnabled()) { _log.info("Poller status:" + status); } } private final Log _log = LogFactoryUtil.getLog(BladePollProcessor.class); }
===================
AuthFailure & Authenticator
com.liferay.portal.security.auth.AuthFailure
集成点:"key" : "auth.failure" | "auth.max.failures"
用途: 用户登录失败处理
com.liferay.portal.security.auth.Authenticator
集成点:"key" : "auth.pipeline.post" | "auth.pipeline.post"
用途: 用户登录验证
例子:
Liferay7 BPM门户开发之32: 实现自定义认证登陆(定制Authentication Hook)
===================
StrutsAction & StrutsPortletAction
com.liferay.portal.kernel.struts.StrutsAction
集成点:"path" : a struts path (starting with "/portal")
用途: HttpServletRequest方式的Struts Action跳转
com.liferay.portal.kernel.struts.StrutsPortletAction
集成点:"path" : a portlet struts path
用途: StrutsPortletAction方式的Struts Action跳转
StrutsAction和StrutsPortletAction都不是官方推荐方式
例子:
@Component( immediate = true, property = {"path=/login/login"}, service = StrutsPortletAction.class ) public class BladePortletAction extends BaseStrutsPortletAction { @Override public void processAction( StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig, ActionRequest actionRequest, ActionResponse actionResponse) throws Exception { if (_log.isDebugEnabled()) { _log.debug("BladePortletAction - procesAction"); } ThemeDisplay themeDisplay = (ThemeDisplay)actionRequest.getAttribute( WebKeys.THEME_DISPLAY); User loggedinUser = themeDisplay.getUser(); if ((loggedinUser != null) && _log.isInfoEnabled()) { _log.info( "Logging in with user:[" + loggedinUser.getFirstName() + " " + loggedinUser.getLastName() + "]"); _log.info( "Logged in user: Current Greetings[" + loggedinUser.getGreeting() + "]"); } originalStrutsPortletAction.processAction( originalStrutsPortletAction, portletConfig, actionRequest, actionResponse); } @Override public String render( StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig, RenderRequest renderRequest, RenderResponse renderResponse) throws Exception { if (_log.isDebugEnabled()) { _log.debug("BladePortletAction - render"); } ThemeDisplay themeDisplay = (ThemeDisplay)renderRequest.getAttribute( WebKeys.THEME_DISPLAY); User loggedinUser = themeDisplay.getUser(); if (loggedinUser != null) { loggedinUser.setLastName("BLADE"); loggedinUser.setGreeting( "Hello," + loggedinUser.getFirstName() + " from BLADE!"); _userLocalService.updateUser(loggedinUser); } return originalStrutsPortletAction.render( originalStrutsPortletAction, portletConfig, renderRequest, renderResponse); } @Override public void serveResource( StrutsPortletAction originalStrutsPortletAction, PortletConfig portletConfig, ResourceRequest resourceRequest, ResourceResponse resourceResponse) throws Exception { if (_log.isDebugEnabled()) { _log.debug("BladePortletAction - serveResource"); } originalStrutsPortletAction.serveResource( originalStrutsPortletAction, portletConfig, resourceRequest, resourceResponse); } @Reference(unbind = "-") public void setUserService(UserLocalService userService) { _userLocalService = userService; } private static final Log _log = LogFactoryUtil.getLog( BladePortletAction.class); private UserLocalService _userLocalService; }
===================
ResourceBundle
java.util.ResourceBundle
集成点:"javax.portlet.name" : a FQPN
用途: 用于国际化字符串
例子:https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-0/localizing-your-application
demo:
import com.liferay.portal.kernel.language.UTF8Control; import java.util.Enumeration; import java.util.ResourceBundle; import org.osgi.service.component.annotations.Component; @Component( immediate = true, property = {"language.id=en_US"}, service = ResourceBundle.class ) public class CustomResourceBundle extends ResourceBundle { @Override public Enumeration<String> getKeys() { return _resourceBundle.getKeys(); } @Override protected Object handleGetObject(String key) { return _resourceBundle.getObject(key); } private final ResourceBundle _resourceBundle = ResourceBundle.getBundle( "content.Language", UTF8Control.INSTANCE); }
===================
ActionCommand
com.liferay.util.bridges.mvc.ActionCommand
集成点:"action.command.name" : an MVCPortlet action command name
集成点:"javax.portlet.name" : a FQPN
用途: Action周期处理
例子:https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-0/mvc-action-command
demo:
@Component( immediate = true, property = { "javax.portlet.name=com_liferay_blade_samples_portlet_actioncommand_GreeterPortlet", "mvc.command.name=greet" }, service = MVCActionCommand.class ) public class GreeterActionCommand implements MVCActionCommand { @Override public boolean processAction( ActionRequest actionRequest, ActionResponse actionResponse) throws PortletException { _handleActionCommand(actionRequest); return true; } private void _handleActionCommand(ActionRequest actionRequest) { String name = ParamUtil.get(actionRequest, "name", StringPool.BLANK); if (_log.isInfoEnabled()) { _log.info("Hello " + name); } String greetingMessage = "Hello " + name + "! Welcome to OSGi"; actionRequest.setAttribute("GREETER_MESSAGE", greetingMessage); SessionMessages.add(actionRequest, "greetingMessage", greetingMessage); } private static final Log _log = LogFactoryUtil.getLog( GreeterActionCommand.class); }
===================
Filter
javax.portlet.filter.ActionFilter
javax.portlet.filter.EventFilter
javax.portlet.filter.RenderFilter
javax.portlet.filter.ResourceFilter
集成点:"javax.portlet.name" : a FQPN
例子:
Liferay7 BPM门户开发之36: 使用Portlet filters过滤器做切面AOP
===================
Social
com.liferay.portlet.social.model.SocialActivityInterpreter
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
com.liferay.portlet.social.model.SocialRequestInterpreter
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
AssetRendererFactory
com.liferay.portlet.asset.model.AssetRendererFactory
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
AtomCollectionAdapter
com.liferay.portal.kernel.atom.AtomCollectionAdapter<?>
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
ConfigurationAction
com.liferay.portal.kernel.portlet.ConfigurationAction
集成点:"javax.portlet.name" : a FQPN
用途: 配置动作扩展
例子:https://dev.liferay.com/develop/tutorials/-/knowledge_base/7-0/implementing-configuration-actions
===================
ControlPanelEntry
com.liferay.portlet.ControlPanelEntry
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
CustomAttributesDisplay
com.liferay.portlet.expando.model.CustomAttributesDisplay
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portlet.dynamicdatamapping.util.DDMDisplay
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.portlet.FriendlyURLMapper
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.search.Indexer
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.search.OpenSearch
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.security.permission.PermissionPropagator
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.pop.MessageListener
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.lar.PortletDataHandler
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.portlet.PortletLayoutListener
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
javax.portlet.PreferencesValidator
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.lar.StagedModelDataHandler
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.template.TemplateHandler
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.trash.TrashHandler
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.servlet.URLEncoder
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.notifications.UserNotificationHandler
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.webdav.WebDAVStorage
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.workflow.WorkflowHandler
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.xmlrpc.Method
集成点:"javax.portlet.name" : a FQPN
用途:
例子:
===================
com.liferay.portal.kernel.events.LifecycleAction
集成点:
用途:
例子:
===================
com.liferay.portal.kernel.search.IndexerPostProcessor
集成点: "indexer.class.name" : a indexer or entity class name
用途:
例子:
===================
com.liferay.portal.service.ServiceWrapper
集成点:
用途:
例子:
待续...
目前维护的开源产品:https://gitee.com/475660