Bootcamp 2013 - SMC

70
Bootcamp 2013 Come sfruttare appieno le potenzialità di Liferay Portal Liferay Italian Partner Ecosystem

Transcript of Bootcamp 2013 - SMC

Bootcamp 2013Come sfruttare appieno le potenzialità di Liferay Portal

Liferay Italian Partner Ecosystem

Social Media

Hashtag ufficiale del bootcamp e del simposio•#LRIS2013

Bootcamp 2012

L’anno scorso abbiamo realizzato un portlet per la gestione di ticket su LR CE.Abbiamo affrontato i seguenti argomenti:

• Introduzione alle specifiche JSR 168 / JSR 286

• Service Builder

• MVC Portlet Framework

• Resource & Asset Framework

• Permission

JSR168 / JSR286

Service Builder

Service Builder

MVC Portlet Framework

Resource & Asset Framework

Permission System

Bootcamp 2013

Quest’anno faremo la sua evoluzione su piattaforma LR EE e introdurremo le funzionalità avanzate:

• Workflow

• Message Bus

• Audit Framework

• Jasper Integration

Liferay Architecture

Workflow

Workflow Approvativo

Message Bus

Message BUS

Sincrono e Asincrono

WORKFLOW APPROVATIVO

<entity name="ProductsEntry" table="HDProductsEntry" uuid="true" local-service="true" remote-service="true">…<!-- Workflow fields --><column name="status" type="int" /><column name="statusByUserId" type="long" /><column name="statusByUserName" type="String" /><column name="statusDate" type="Date" />…<!-- Finder methods --><finder name="G_S" return-type="Collection" >

<finder-column name="groupId" /><finder-column name="status" />

</finder>…</entity>

service.xml

liferay-portlet.xml

<liferay-portlet-app>

<portlet>

<workflow-handler>

it.bootcamp.liferay.helpdesk.products.workflow.ProductsEntryWorkflowHandler

</workflow-handler>

</portlet>

…</liferay-portlet-app>

La classe in questione deve estendere com.liferay.portal.kernel.workflow.BaseWorkflowHandler

public class ProductsEntryWorkflowHandler extends BaseWorkflowHandler {

public String getClassName() {

return _CLASS_NAME;

}

public String getType(Locale locale) {

return ResourceActionsUtil.getModelResource(locale, _CLASS_NAME);

}

public Object updateStatus(

int status, Map<String, Serializable> workflowContext){

}

private static final String _CLASS_NAME = ProductsEntry.class.getName();

ProductsEntryWorkflowHandler - parte 1

public Object updateStatus(int status, Map<String, Serializable> workflowContext){

long userId = GetterUtil.getLong(

(String)workflowContext.get(WorkflowConstants.CONTEXT_USER_ID));

long classPK = GetterUtil.getLong((String)workflowContext.get(

WorkflowConstants.CONTEXT_ENTRY_CLASS_PK));

ServiceContext serviceContext = (ServiceContext)workflowContext.get(

"serviceContext");

return ProductsEntryLocalServiceUtil.updateStatus(

userId, classPK, status, serviceContext);}

ProductsEntryWorkflowHandler - parte 2

-- VISUALIZZARE IN ECLIPSE IL LOCAL SERVICE SUL METODO UPDATE STATUS--

ProductsEntryLocalServiceImpl

/*Verificare se l’asset Product entry ha un workflow associato*/boolean workflowIsActive = WorkflowDefinitionLinkLocalServiceUtil.hasWorkflowDefinitionLink(

themeDisplay.getCompanyId(), scopeGroupId, ProductsEntry.class.getName());

Esempio di utility del workflow

/*Verificare se l’asset Product entry ha un workflow associato*/boolean workflowIsActive = WorkflowDefinitionLinkLocalServiceUtil.hasWorkflowDefinitionLink(

themeDisplay.getCompanyId(), scopeGroupId, ProductsEntry.class.getName());/*Assegnare un workflow ad un utente*/WorkflowTaskManagerUtil.assignWorkflowTaskToUser(

companyId, userId, workflowTask.getWorkflowTaskId(),assigneeUserId, comment, null, null)

Esempio di utility del workflow

/*Verificare se l’asset Product entry ha un workflow associato*/boolean workflowIsActive = WorkflowDefinitionLinkLocalServiceUtil.hasWorkflowDefinitionLink(

themeDisplay.getCompanyId(), scopeGroupId, ProductsEntry.class.getName());/*Assegnare un workflow ad un utente*/WorkflowTaskManagerUtil.assignWorkflowTaskToUser(

companyId, userId, workflowTask.getWorkflowTaskId(),assigneeUserId, comment, null, null)

/*Recuperare l’elenco dei task attivi su un Product Entry*/WorkflowInstanceLink workflowInstanceLink = WorkflowInstanceLinkLocalServiceUtil.getWorkflowInstanceLink(

companyId, groupId, className, classPk);long workflowInstanceId = workflowInstanceLink.getWorkflowInstanceId();WorkflowInstance workflowInstance =

WorkflowInstanceManagerUtil.getWorkflowInstance(companyId, workflowInstanceId);

List<WorkflowTask> taskList = WorkflowTaskManagerUtil.getWorkflowTasksByWorkflowInstance(

companyId, null, workflowInstance.getWorkflowInstanceId(),false, -1, -1, null);

Esempio di utility del workflow

/*Verificare se l’asset Product entry ha un workflow associato*/boolean workflowIsActive = WorkflowDefinitionLinkLocalServiceUtil.hasWorkflowDefinitionLink(

themeDisplay.getCompanyId(), scopeGroupId, ProductsEntry.class.getName());/*Assegnare un workflow ad un utente*/WorkflowTaskManagerUtil.assignWorkflowTaskToUser(

companyId, userId, workflowTask.getWorkflowTaskId(),assigneeUserId, comment, null, null)

/*Recuperare l’elenco dei task attivi su un Product Entry*/WorkflowInstanceLink workflowInstanceLink = WorkflowInstanceLinkLocalServiceUtil.getWorkflowInstanceLink(

companyId, groupId, className, classPk);long workflowInstanceId = workflowInstanceLink.getWorkflowInstanceId();WorkflowInstance workflowInstance =

WorkflowInstanceManagerUtil.getWorkflowInstance(companyId, workflowInstanceId);

List<WorkflowTask> taskList = WorkflowTaskManagerUtil.getWorkflowTasksByWorkflowInstance(

companyId, null, workflowInstance.getWorkflowInstanceId(),false, -1, -1, null);

/*Avviare un workflow*/WorkflowHandlerRegistryUtil.startWorkflowInstance(

user.getCompanyId(), groupId, userId, ProductsEntry.class.getName(),entry.getProductsEntryId(), entry, serviceContext);

Esempio di utility del workflow

DEMO

DEMO Parte 1• Visualizzazione portlet Help Desk senza workflow associato;• Presentazione portlet di control panel per la gestione del workflow;• Design grafico di un workflow;• Visualizzazione portlet Help Desk con workflow associato;

Draft e Publish di un workflow - parte 1

<aui:form action="<%= editEntryURL %>" method="post" name="fm" onSubmit='<%= "event.preventDefault(); " %>‘ >

if(WorkflowDefinitionLinkLocalServiceUtil.hasWorkflowDefinitionLink(themeDisplay.getCompanyId(),

scopeGroupId,ProductsEntry.class.getName())) {

publishButtonLabel = "submit-for-publication";

}

<aui:input name="workflowAction" type="hidden" value="<%= WorkflowConstants.ACTION_SAVE_DRAFT %>" />

<aui:button disabled="<%=closed%>" name="saveButton" onClick='<%= renderResponse.getNamespace() + "saveEntry(true);" %>'

type="submit" value="<%= saveButtonLabel %>" />

<aui:button disabled="<%= pending || closed%>" name="publishButton" onClick='<%=renderResponse.getNamespace()+"saveEntry(false);"

%>' type="submit" value="<%= publishButtonLabel %>" />

…</aui:form>

Draft e Publish di un workflow - parte 2

<aui:script>function <portlet:namespace />saveEntry(draft){

if (!draft) {

document.<portlet:namespace />fm.<portlet:namespace /> workflowAction.value = "<%= WorkflowConstants.ACTION_PUBLISH %>";

}

submitForm(document.<portlet:namespace />fm);}</aui:script>

Draft e Publish di un workflow – parte 3

-- VISUALIZZARE IN ECLIPSE LA PORTLET, IL LOCAL E REMOTE SERVICE --

DEMO

DEMO parte 2- Avvio di un worfklow con codice licenza non valido;- Avvio di un workflow con licenza valida;-Come visualizzare lo stato del workflow utilizzando le portlet standard liferay e come includere i dati del workflow nelle proprie jsp;-Completamento del workflow con assegnazione all’operatore.

MESSAGE BUS

MESSAGE BUS – WORKFLOW INTEGRATION

BUSINESS CASE

•Submit Help Desk Issue

•Controllo License Code su sistema esterno

• esistenza contratto

• regolarità pagamenti

• ecc…

•Cambio di stato su WF

MESSAGE BUS

LIFERAY PORTAL

CRMERP

ESB

MESSAGE BUS

WF AUDIT

LEGACY

ASSET

FLUSSO SINCRONO

web.xml

messaging-spring.xml

web.xml

messaging-spring.xml

web.xml

messaging-spring.xml

messaging-spring.xml

messaging-spring.xml

messaging-spring.xml

Workflow code

Workflow code

MessageListener

MessageListener

MessageListener

DEMO KALEO FORM

•Come creare un nuovo processo;•Come definire i form;•Come modificare o creare un workflow;•Come associare i form ai task del workflow;

ASINCRONO

messaging-spring.xml

messaging-spring.xml

Workflow code

Message Listener

Enterprise Service BUS

Reply Service

service.xmlservice.xml

Reply Service

service.xmlservice.xml

Service Interface

ServiceImpl

ServiceImpl

ServiceImpl

DEMO FLUSSO ASINCRONO

Advanced Topic

•Cache

• A livello Message Bus Liferay

• A livello Enterprise Service BUS

•Disaccoppiamento tra Message BUS Liferay e ESB: mantenibilità e flessibilità del progetto.

•Modello Asincrono – eventi in real time di eventi non solo del portale, ma anche dei sistemi informativi presenti in azienda dietro il portale. Tutto ciò grazie al disaccoppiamento dato dal MB.

Advanced Topic

•Cache

• A livello Message Bus Liferay

• A livello Enterprise Service BUS

•Disaccoppiamento tra Message BUS Liferay e ESB: mantenibilità e flessibilità del progetto.

•Modello Asincrono – eventi in real time di eventi non solo del portale, ma anche dei sistemi informativi presenti in azienda dietro il portale. Tutto ciò grazie al disaccoppiamento dato dal MB.

Advanced Topic

•Cache

• A livello Message Bus Liferay

• A livello Enterprise Service BUS

•Disaccoppiamento tra Message BUS Liferay e ESB: mantenibilità e flessibilità del progetto.

•Modello Asincrono – eventi in real time di eventi non solo del portale, ma anche dei sistemi informativi presenti in azienda dietro il portale. Tutto ciò grazie al disaccoppiamento dato dal MB.

Coffee ….

Audit

Auditing

L’esigenza

Audit trail (o audit log): record cronologico che fornisce la prova documentata della sequenza delle attività svolte all'interno di un sistema informatico;Registrare le azioni compiute dagli utenti:

Sicurezza

Gestione informazioni sensibili

Vincolo di dominio o normativo

Presenza di operatori con privilegi di amministrazione

Audit Service in Liferay

Meccanismo flessibile per l'inserimento dei messaggi di audit:

Message bus (liferay/audit)

PluginsAudit Plugin (EE):

Audit Service

Audit ReportsAudit Hook

Componenti Audit Hook

Audit Message

com.liferay.portal.kernel.audit.AuditMessage

message, eventType, companyId, userId, userName, className, classPK, type, sessionID, clientIP, serverIP, timestamp, additionalInfo

portal.propertiescom.liferay.portal.servlet.filters.audit.AuditFilter=true

Componenti Audit Service

AuditMessageBuilder

public static AuditMessage buildAuditMessage( String eventType, String className, long classPK, List<Attribute> attributes) { // recuperiamo il companyId e lo userId AuditRequestThreadLocal auditRequestThreadLocal = AuditRequestThreadLocal.getAuditThreadLocal(); long realUserId = auditRequestThreadLocal.getRealUserId(); String realUserName = PortalUtil.getUserName( realUserId, StringPool.BLANK); JSONObject additionalInfo = JSONFactoryUtil.createJSONObject(); if ((realUserId > 0) && (userId != realUserId)) { additionalInfo.put("doAsUserId", String.valueOf(userId)); additionalInfo.put( "doAsUserName", PortalUtil.getUserName(userId, StringPool.BLANK)); } // popoliamo ulteriormente additionalInfo se necessario return new AuditMessage( eventType, companyId, realUserId, realUserName, className, String.valueOf(classPK), null, additionalInfo); } }

ProductsEntryModelListener - creazione e rimozione

protected void auditOnCreateOrRemove(String eventType, ProductsEntry productsEntry) throws ModelListenerException { try { AuditMessage auditMessage = AuditMessageBuilder.buildAuditMessage( eventType, ProductsEntry.class.getName(), productsEntry.getProductsEntryId(), null); AuditRouterUtil.route(auditMessage); } catch (Exception e) { throw new ModelListenerException(e); }}

ProductsEntryModelListener - modifica

public void onBeforeUpdate(ProductsEntry newProductsEntry) throws ModelListenerException { try { //Get the current model values from the DB ProductsEntry oldProductsEntry = ProductsEntryLocalServiceUtil.getProductsEntry( newProductsEntry.getProductsEntryId()); //Collects the modified attributes to audit List<Attribute> attributes = getModifiedAttributes( newProductsEntry, oldProductsEntry); if (!attributes.isEmpty()) { AuditMessage auditMessage = AuditMessageBuilder.buildAuditMessage( EventTypes.UPDATE, ProductsEntry.class.getName(), newProductsEntry.getProductsEntryId(), attributes); AuditRouterUtil.route(auditMessage); } } catch (Exception e) { throw new ModelListenerException(e); }}

Grazie!