bootcamp 2013 - smc
TRANSCRIPT
Bootcamp 2013Come sfruttare appieno le potenzialità di Liferay Portal
Liferay Italian Partner Ecosystem
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
Bootcamp 2013
Quest’anno faremo la sua evoluzione su piattaforma LR EE e introdurremo le funzionalità avanzate:
• Workflow
• Message Bus
• Audit Framework
• Jasper Integration
<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 – WORKFLOW INTEGRATION
BUSINESS CASE
•Submit Help Desk Issue
•Controllo License Code su sistema esterno
• esistenza contratto
• regolarità pagamenti
• ecc…
•Cambio di stato su WF
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;
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.
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
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
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); }}