sexta-feira, 20 de março de 2009

"Injetando" funcionalidades nas páginas do JBoss Portal

Este é meu segundo Post sobre JBoss Portal neste ano, e irá abordar uma forma diferenciada de "injetar" determinada funcionalidade a uma área do layout do seu site usando AOP com uma implementação de JBossInterceptor.

O JBoss Portal disponibiliza uma API de Injeção de Dependências onde é possível criar interceptadores a partir da classe abstrata JBossInterceptor. A classe ControllerInterceptor (q extends JBossInterceptor), é utilizada como base para adicionar segurança em commands, navegação, menus, header, dashboard, etc...
É possível criar seu Aspecto para o JBoss Portal herdando ControllerInterceptor e criando seu método invoke.

Para exemplificar, iremos adicionar um título ao layout do Portal utilizando o nome de cada página criado via interface administrativa em uma área pré-definida no layout utilizando o Interceptador PageCustomizerInterceptor, o qual já está sendo utilizado para outros fins. Resumindo, iremos alterar o PageCustomizerInterceptor para que a partir dele os títulos sejam incluídos dinamicamente.

O Código 1 define o MBean PageCustomizer com o atributo Title, onde é passado o Path da página que irá exibir o Título da página.

Código 1
   <mbean code="org.jboss.portal.core.aspects.controller.PageCustomizerInterceptor"
name="portal:service=Interceptor,type=Command,name=PageCustomizer" xmbean-dd=""
xmbean-code="org.jboss.portal.jems.as.system.JBossServiceModelMBean">
<xmbean/>
<attribute name="TargetContextPath">/portal-core</attribute>
<attribute name="HeaderPath">/WEB-INF/jsp/header/header.jsp</attribute>
<attribute name="TabsPath">/WEB-INF/jsp/header/tabs.jsp</attribute>
<attribute name="Breadcrumbs">/WEB-INF/jsp/header/breadcrumbs.jsp</attribute>
<attribute name="Title">/WEB-INF/jsp/header/title.jsp</attribute>
<!-- Overrides the value of core.login.namespace in config.xml --><!-- attribute name="LoginNamespace">dashboard</attribute -->
<depends optional-attribute-name="Config" proxy-type="attribute">portal:service=ServerConfig</depends>
<depends optional-attribute-name="PortalAuthorizationManagerFactory" proxy-type="attribute">
portal:service=PortalAuthorizationManagerFactory
</depends>
<depends optional-attribute-name="PortalObjectContainer" proxy-type="attribute">portal:container=PortalObject
</depends>
</mbean>

O Código 2 mostra a customização da classe PageCustomizerInterceptor onde foi criado o atributo de classe privado chamado title e seus métodos get e set. Este atributo está associado ao Title definido no MBean e será utilizado pelo método injectTitle para fazer o redirecionamento para o jsp de exibição do título. Além disso, o método injectTitle também adiciona dois atributos a requisição: org.jboss.portal.api.PORTAL_NODE e org.jboss.portal.api.PORTAL_RUNTIME_CONTEXT, com valor do Nó corrente e do contexto respectivamente. O método invoke é utilizado para montar a página com suas respectivas propriedades.

Código 2
public class PageCustomizerInterceptor extends ControllerInterceptor {
...

/** title */
private String title;

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

...

public ControllerResponse invoke(ControllerCommand cmd) throws Exception {
ControllerResponse resp = (ControllerResponse)cmd.invokeNext();

// Insert navigation portlet in the page
if (resp instanceof PageRendition) {
PageRendition rendition = (PageRendition)resp;

if (cmd instanceof PageCommand) {
PageCommand rpc = (PageCommand)cmd;

String tabbedTitle = injectTitle(rpc);
if (tabbedTitle != null) {
Map windowProps = new HashMap();
windowProps.put(ThemeConstants.PORTAL_PROP_WINDOW_RENDERER, "emptyRenderer");
windowProps.put(ThemeConstants.PORTAL_PROP_DECORATION_RENDERER, "emptyRenderer");
windowProps.put(ThemeConstants.PORTAL_PROP_PORTLET_RENDERER, "emptyRenderer");
WindowResult res = new WindowResult("", tabbedTitle, Collections.EMPTY_MAP, windowProps, null, WindowState.NORMAL, Mode.VIEW);
WindowContext blah = new WindowContext("BLAH", "tituloPrincipal", "0", res);
rendition.getPageResult().addWindowContext(blah);

//
Region region = rendition.getPageResult().getRegion2("tituloPrincipal");
DynaRenderOptions.NO_AJAX.setOptions(region.getProperties());
}
}
}

return resp;
}

...

public String injectTitle(PageCommand rpc) {
ControllerContext controllerCtx = rpc.getControllerContext();
ControllerRequestDispatcher rd = controllerCtx.getRequestDispatcher(targetContextPath, title);

if (rd != null) {
Page page = rpc.getPage();
PortalAuthorizationManager pam = portalAuthorizationManagerFactory.getManager();
PortalNodeImpl node = new PortalNodeImpl(pam, page);

rd.setAttribute("org.jboss.portal.api.PORTAL_NODE", node);
rd.setAttribute("org.jboss.portal.api.PORTAL_RUNTIME_CONTEXT", Navigation.getPortalRuntimeContext());

rd.include();
return rd.getMarkup();
}

return null;
}

...

}

O Código 3 mostra o JSP que exibe o título da página.

Código 3
<%@ page import="org.jboss.portal.api.node.PortalNode" %>
<%@ page import="java.util.Locale" %>
<%
PortalNode node = (PortalNode)request.getAttribute("org.jboss.portal.api.PORTAL_NODE");

// Get a locale
Locale locale = request.getLocale();
if (locale == null)
{
locale = Locale.getDefault();
}
%>
<%= node.getDisplayName(locale) %>

O Código 4 mostra a chamada a taglib para encaixar o título no layout desejado.

Código 4
<p:region regionName='tituloPrincipal' regionID='tituloPrincipal'/>


Considerações finais
Este post tratou uma forma flexível de inclusão de funcionalidades ao layout do JBoss Portal. Atualmente já é utilizada para outras áreas, mas, além da inclusão de títulos dinâmicos, pode ser totalmente adaptada para inclusão de breadcrumbs, por exemplo, principalmente para evitar ter que incluir seu respectivo Portlet em todas as páginas necessárias manualmente, economizando tempo precioso de desenvolvimento.