/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.workflow.kaleo.definition.internal.parser;
import com.liferay.portal.kernel.model.RoleConstants;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.ListUtil;
import com.liferay.portal.kernel.util.StringUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.workflow.WorkflowDefinitionFileException;
import com.liferay.portal.kernel.workflow.WorkflowException;
import com.liferay.portal.kernel.xml.Document;
import com.liferay.portal.kernel.xml.Element;
import com.liferay.portal.kernel.xml.SAXReaderUtil;
import com.liferay.portal.workflow.kaleo.definition.Action;
import com.liferay.portal.workflow.kaleo.definition.ActionAware;
import com.liferay.portal.workflow.kaleo.definition.AddressRecipient;
import com.liferay.portal.workflow.kaleo.definition.AssigneesRecipient;
import com.liferay.portal.workflow.kaleo.definition.Assignment;
import com.liferay.portal.workflow.kaleo.definition.Condition;
import com.liferay.portal.workflow.kaleo.definition.Definition;
import com.liferay.portal.workflow.kaleo.definition.DelayDuration;
import com.liferay.portal.workflow.kaleo.definition.DurationScale;
import com.liferay.portal.workflow.kaleo.definition.Fork;
import com.liferay.portal.workflow.kaleo.definition.Join;
import com.liferay.portal.workflow.kaleo.definition.JoinXor;
import com.liferay.portal.workflow.kaleo.definition.Node;
import com.liferay.portal.workflow.kaleo.definition.Notification;
import com.liferay.portal.workflow.kaleo.definition.NotificationAware;
import com.liferay.portal.workflow.kaleo.definition.NotificationReceptionType;
import com.liferay.portal.workflow.kaleo.definition.ResourceActionAssignment;
import com.liferay.portal.workflow.kaleo.definition.RoleAssignment;
import com.liferay.portal.workflow.kaleo.definition.RoleRecipient;
import com.liferay.portal.workflow.kaleo.definition.ScriptAssignment;
import com.liferay.portal.workflow.kaleo.definition.ScriptRecipient;
import com.liferay.portal.workflow.kaleo.definition.State;
import com.liferay.portal.workflow.kaleo.definition.Task;
import com.liferay.portal.workflow.kaleo.definition.TaskForm;
import com.liferay.portal.workflow.kaleo.definition.TaskFormReference;
import com.liferay.portal.workflow.kaleo.definition.Timer;
import com.liferay.portal.workflow.kaleo.definition.Transition;
import com.liferay.portal.workflow.kaleo.definition.UserAssignment;
import com.liferay.portal.workflow.kaleo.definition.UserRecipient;
import com.liferay.portal.workflow.kaleo.definition.parser.WorkflowModelParser;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
/**
* @author Michael C. Han
* @author Marcellus Tavares
* @author Eduardo Lundgren
*/
@Component(immediate = true, service = WorkflowModelParser.class)
public class XMLWorkflowModelParser implements WorkflowModelParser {
@Override
public Definition parse(InputStream inputStream) throws WorkflowException {
try {
Document document = SAXReaderUtil.read(inputStream, _validate);
return doParse(document);
}
catch (Exception e) {
throw new WorkflowDefinitionFileException(
"Unable to parse definition", e);
}
}
@Override
public Definition parse(String content) throws WorkflowException {
try {
Document document = SAXReaderUtil.read(content, _validate);
return doParse(document);
}
catch (Exception e) {
throw new WorkflowDefinitionFileException(
"Unable to parse definition", e);
}
}
public void setValidate(boolean validate) {
_validate = validate;
}
@Activate
protected void activate(Map<String, Object> properties) {
_validate = GetterUtil.getBoolean(properties.get("validating"), true);
}
protected Definition doParse(Document document) throws Exception {
Element rootElement = document.getRootElement();
String name = rootElement.elementTextTrim("name");
String description = rootElement.elementTextTrim("description");
int version = GetterUtil.getInteger(
rootElement.elementTextTrim("version"));
Definition definition = new Definition(
name, description, document.formattedString(), version);
List<Element> conditionElements = rootElement.elements("condition");
for (Element conditionElement : conditionElements) {
Condition condition = parseCondition(conditionElement);
definition.addNode(condition);
}
List<Element> forkElements = rootElement.elements("fork");
for (Element forkElement : forkElements) {
Fork fork = parseFork(forkElement);
definition.addNode(fork);
}
List<Element> joinElements = rootElement.elements("join");
for (Element joinElement : joinElements) {
Join join = parseJoin(joinElement);
definition.addNode(join);
}
List<Element> joinXorElements = rootElement.elements("join-xor");
for (Element joinXorElement : joinXorElements) {
JoinXor joinXor = parseJoinXor(joinXorElement);
definition.addNode(joinXor);
}
List<Element> stateElements = rootElement.elements("state");
for (Element stateElement : stateElements) {
State state = parseState(stateElement);
definition.addNode(state);
}
List<Element> taskElements = rootElement.elements("task");
for (Element taskElement : taskElements) {
Task task = parseTask(taskElement);
definition.addNode(task);
}
parseTransitions(
definition, conditionElements, forkElements, joinElements,
joinXorElements, stateElements, taskElements);
return definition;
}
protected void parseActionElements(
List<Element> actionElements, ActionAware actionAware) {
if (actionElements.isEmpty()) {
return;
}
Set<Action> actions = new HashSet<>(actionElements.size());
for (Element actionElement : actionElements) {
String name = actionElement.elementTextTrim("name");
String description = actionElement.elementTextTrim("description");
String executionType = actionElement.elementTextTrim(
"execution-type");
String script = actionElement.elementText("script");
String scriptLanguage = actionElement.elementTextTrim(
"script-language");
String scriptRequiredContexts = actionElement.elementTextTrim(
"script-required-contexts");
int priority = GetterUtil.getInteger(
actionElement.elementTextTrim("priority"));
Action action = new Action(
name, description, executionType, script, scriptLanguage,
scriptRequiredContexts, priority);
actions.add(action);
}
actionAware.setActions(actions);
}
protected void parseActionsElement(Element actionsElement, Node node) {
if (actionsElement == null) {
return;
}
List<Element> actionElements = actionsElement.elements("action");
parseActionElements(actionElements, node);
List<Element> notificationElements = actionsElement.elements(
"notification");
parseNotificationElements(notificationElements, node);
}
protected Set<Assignment> parseAssignments(Element assignmentsElement) {
if (assignmentsElement == null) {
return Collections.emptySet();
}
Set<Assignment> assignments = new HashSet<>();
Element resourceActionsElement = assignmentsElement.element(
"resource-actions");
if (resourceActionsElement != null) {
List<Element> resourceActionElements =
resourceActionsElement.elements("resource-action");
for (Element resourceActionElement : resourceActionElements) {
String actionId = resourceActionElement.getTextTrim();
if (Validator.isNotNull(actionId)) {
ResourceActionAssignment resourceActionAssignment =
new ResourceActionAssignment(actionId);
assignments.add(resourceActionAssignment);
}
}
}
Element rolesElement = assignmentsElement.element("roles");
if (rolesElement != null) {
List<Element> roleAssignmentElements = rolesElement.elements(
"role");
for (Element roleAssignmentElement : roleAssignmentElements) {
long roleId = GetterUtil.getLong(
roleAssignmentElement.elementTextTrim("role-id"));
String roleType = GetterUtil.getString(
roleAssignmentElement.elementTextTrim("role-type"),
RoleConstants.TYPE_REGULAR_LABEL);
String name = roleAssignmentElement.elementTextTrim("name");
RoleAssignment roleAssignment = null;
if (Validator.isNotNull(name)) {
roleAssignment = new RoleAssignment(name, roleType);
boolean autoCreate = GetterUtil.getBoolean(
roleAssignmentElement.elementTextTrim("auto-create"),
true);
roleAssignment.setAutoCreate(autoCreate);
}
else {
roleAssignment = new RoleAssignment(roleId);
}
assignments.add(roleAssignment);
}
}
List<Element> scriptedAssignmentElements = assignmentsElement.elements(
"scripted-assignment");
for (Element scriptedAssignmentElement : scriptedAssignmentElements) {
String script = scriptedAssignmentElement.elementText("script");
String scriptLanguage = scriptedAssignmentElement.elementTextTrim(
"script-language");
String scriptRequiredContexts =
scriptedAssignmentElement.elementTextTrim(
"script-required-contexts");
ScriptAssignment scriptAssignment = new ScriptAssignment(
script, scriptLanguage, scriptRequiredContexts);
assignments.add(scriptAssignment);
}
List<Element> userAssignmentElements = assignmentsElement.elements(
"user");
for (Element userAssignmentElement : userAssignmentElements) {
long userId = GetterUtil.getLong(
userAssignmentElement.elementTextTrim("user-id"));
String screenName = userAssignmentElement.elementTextTrim(
"screen-name");
String emailAddress = userAssignmentElement.elementTextTrim(
"email-address");
UserAssignment userAssignment = new UserAssignment(
userId, screenName, emailAddress);
assignments.add(userAssignment);
}
return assignments;
}
protected Condition parseCondition(Element conditionElement) {
String name = conditionElement.elementTextTrim("name");
String description = conditionElement.elementTextTrim("description");
String script = conditionElement.elementText("script");
String scriptLanguage = conditionElement.elementTextTrim(
"script-language");
String scriptRequiredContexts = conditionElement.elementTextTrim(
"script-required-contexts");
Condition condition = new Condition(
name, description, script, scriptLanguage, scriptRequiredContexts);
String metadata = conditionElement.elementTextTrim("metadata");
condition.setMetadata(metadata);
Element actionsElement = conditionElement.element("actions");
parseActionsElement(actionsElement, condition);
Element timersElement = conditionElement.element("timers");
parseTimerElements(timersElement, condition);
return condition;
}
protected DelayDuration parseDelay(Element delayElement) {
if (delayElement == null) {
return null;
}
double duration = GetterUtil.getDouble(
delayElement.elementTextTrim("duration"));
DurationScale durationScale = DurationScale.valueOf(
StringUtil.toUpperCase(delayElement.elementTextTrim("scale")));
return new DelayDuration(duration, durationScale);
}
protected Fork parseFork(Element forkElement) {
String name = forkElement.elementTextTrim("name");
String description = forkElement.elementTextTrim("description");
Fork fork = new Fork(name, description);
String metadata = forkElement.elementTextTrim("metadata");
fork.setMetadata(metadata);
Element actionsElement = forkElement.element("actions");
parseActionsElement(actionsElement, fork);
Element timersElement = forkElement.element("timers");
parseTimerElements(timersElement, fork);
return fork;
}
protected Join parseJoin(Element joinElement) {
String name = joinElement.elementTextTrim("name");
String description = joinElement.elementTextTrim("description");
Join join = new Join(name, description);
String metadata = joinElement.elementTextTrim("metadata");
join.setMetadata(metadata);
Element actionsElement = joinElement.element("actions");
parseActionsElement(actionsElement, join);
Element timersElement = joinElement.element("timers");
parseTimerElements(timersElement, join);
return join;
}
protected JoinXor parseJoinXor(Element joinXorElement) {
String name = joinXorElement.elementTextTrim("name");
String description = joinXorElement.elementTextTrim("description");
JoinXor joinXor = new JoinXor(name, description);
String metadata = joinXorElement.elementTextTrim("metadata");
joinXor.setMetadata(metadata);
Element actionsElement = joinXorElement.element("actions");
parseActionsElement(actionsElement, joinXor);
Element timersElement = joinXorElement.element("timers");
parseTimerElements(timersElement, joinXor);
return joinXor;
}
protected void parseNotificationElements(
List<Element> notificationElements,
NotificationAware notificationAware) {
if (notificationElements.isEmpty()) {
return;
}
Set<Notification> notifications = new HashSet<>(
notificationElements.size());
for (Element notificationElement : notificationElements) {
String name = notificationElement.elementTextTrim("name");
String description = notificationElement.elementTextTrim(
"description");
String executionType = notificationElement.elementTextTrim(
"execution-type");
String template = notificationElement.elementTextTrim("template");
String templateLanguage = notificationElement.elementTextTrim(
"template-language");
Notification notification = new Notification(
name, description, executionType, template, templateLanguage);
List<Element> notificationTypeElements =
notificationElement.elements("notification-type");
for (Element notificationTypeElement : notificationTypeElements) {
notification.addNotificationType(
notificationTypeElement.getTextTrim());
}
List<Element> recipientsElements = notificationElement.elements(
"recipients");
for (Element recipientsElement : recipientsElements) {
parseRecipients(
recipientsElement, notification,
NotificationReceptionType.parse(
recipientsElement.attributeValue("receptionType")));
}
notifications.add(notification);
}
notificationAware.setNotifications(notifications);
}
protected void parseRecipients(
Element recipientsElement, Notification notification,
NotificationReceptionType notificationReceptionType) {
if (recipientsElement == null) {
return;
}
List<Element> addressRecipientElements = recipientsElement.elements(
"address");
for (Element addressRecipientElement : addressRecipientElements) {
AddressRecipient addressRecipient = new AddressRecipient(
addressRecipientElement.getTextTrim());
addressRecipient.setNotificationReceptionType(
notificationReceptionType);
notification.addRecipients(addressRecipient);
}
Element assigneesRecipientElement = recipientsElement.element(
"assignees");
if (assigneesRecipientElement != null) {
AssigneesRecipient assigneesRecipient = new AssigneesRecipient();
assigneesRecipient.setNotificationReceptionType(
notificationReceptionType);
notification.addRecipients(assigneesRecipient);
}
Element rolesElement = recipientsElement.element("roles");
if (rolesElement != null) {
List<Element> roleReceipientElements = rolesElement.elements(
"role");
for (Element roleReceipientElement : roleReceipientElements) {
long roleId = GetterUtil.getLong(
roleReceipientElement.elementTextTrim("role-id"));
String roleType = GetterUtil.getString(
roleReceipientElement.elementTextTrim("role-type"),
RoleConstants.TYPE_REGULAR_LABEL);
String name = roleReceipientElement.elementTextTrim("name");
RoleRecipient roleRecipient = null;
if (roleId > 0) {
roleRecipient = new RoleRecipient(roleId, roleType);
}
else {
roleRecipient = new RoleRecipient(name, roleType);
boolean autoCreate = GetterUtil.getBoolean(
roleReceipientElement.elementTextTrim("auto-create"),
true);
roleRecipient.setAutoCreate(autoCreate);
}
roleRecipient.setNotificationReceptionType(
notificationReceptionType);
notification.addRecipients(roleRecipient);
}
}
List<Element> scriptedRecipientElements = recipientsElement.elements(
"scripted-recipient");
for (Element scriptedRecipientElement : scriptedRecipientElements) {
String script = scriptedRecipientElement.elementText("script");
String scriptLanguage = scriptedRecipientElement.elementTextTrim(
"script-language");
String scriptRequiredContexts =
scriptedRecipientElement.elementTextTrim(
"script-required-contexts");
ScriptRecipient scriptRecipient = new ScriptRecipient(
script, scriptLanguage, scriptRequiredContexts);
scriptRecipient.setNotificationReceptionType(
notificationReceptionType);
notification.addRecipients(scriptRecipient);
}
List<Element> userRecipientElements = recipientsElement.elements(
"user");
for (Element userRecipientElement : userRecipientElements) {
long userId = GetterUtil.getLong(
userRecipientElement.elementTextTrim("user-id"));
String screenName = userRecipientElement.elementTextTrim(
"screen-name");
String emailAddress = userRecipientElement.elementTextTrim(
"email-address");
UserRecipient userRecipient = new UserRecipient(
userId, screenName, emailAddress);
userRecipient.setNotificationReceptionType(
notificationReceptionType);
notification.addRecipients(userRecipient);
}
}
protected State parseState(Element stateElement) {
String name = stateElement.elementTextTrim("name");
String description = stateElement.elementTextTrim("description");
boolean initial = GetterUtil.getBoolean(
stateElement.elementTextTrim("initial"));
State state = new State(name, description, initial);
String metadata = stateElement.elementTextTrim("metadata");
state.setMetadata(metadata);
Element actionsElement = stateElement.element("actions");
parseActionsElement(actionsElement, state);
Element timersElement = stateElement.element("timers");
parseTimerElements(timersElement, state);
return state;
}
protected Task parseTask(Element taskElement) {
String name = taskElement.elementTextTrim("name");
String description = taskElement.elementTextTrim("description");
Task task = new Task(name, description);
String metadata = taskElement.elementTextTrim("metadata");
task.setMetadata(metadata);
Element actionsElement = taskElement.element("actions");
parseActionsElement(actionsElement, task);
Element assignmentsElement = taskElement.element("assignments");
if (assignmentsElement != null) {
Set<Assignment> assignments = parseAssignments(assignmentsElement);
task.setAssignments(assignments);
}
Element formsElement = taskElement.element("task-forms");
parseTaskFormsElements(formsElement, task);
Element timersElement = taskElement.element("task-timers");
parseTaskTimerElements(timersElement, task);
return task;
}
protected void parseTaskFormsElements(Element taskFormsElement, Task task) {
if (taskFormsElement == null) {
return;
}
List<Element> taskFormElements = taskFormsElement.elements("task-form");
if (ListUtil.isEmpty(taskFormElements)) {
return;
}
for (Element taskFormElement : taskFormElements) {
String name = taskFormElement.elementTextTrim("name");
int priority = GetterUtil.getInteger(
taskFormElement.elementTextTrim("priority"));
TaskForm taskForm = new TaskForm(name, priority);
String description = taskFormElement.elementTextTrim("description");
if (Validator.isNotNull(description)) {
taskForm.setDescription(description);
}
String formDefinition = taskFormElement.elementTextTrim(
"form-definition");
if (Validator.isNotNull(formDefinition)) {
taskForm.setFormDefinition(formDefinition);
}
else {
Element formReferenceElement = taskFormElement.element(
"form-reference");
TaskFormReference taskFormReference = new TaskFormReference();
long companyId = GetterUtil.getLong(
formReferenceElement.elementTextTrim("company-id"));
taskFormReference.setCompanyId(companyId);
long groupId = GetterUtil.getLong(
formReferenceElement.elementTextTrim("group-id"));
taskFormReference.setGroupId(groupId);
long formId = GetterUtil.getLong(
formReferenceElement.elementTextTrim("form-id"));
taskFormReference.setFormId(formId);
String formUuid = formReferenceElement.elementTextTrim(
"form-uuid");
taskFormReference.setFormUuid(formUuid);
taskForm.setTaskFormReference(taskFormReference);
}
String metadata = taskFormElement.elementTextTrim("metadata");
if (Validator.isNotNull(metadata)) {
taskForm.setMetadata(metadata);
}
task.addTaskForm(taskForm);
}
}
protected void parseTaskTimerElements(
Element taskTimersElement, Node node) {
if (taskTimersElement == null) {
return;
}
List<Element> taskTimerElements = taskTimersElement.elements(
"task-timer");
if (taskTimerElements.isEmpty()) {
return;
}
Set<Timer> timers = new HashSet<>(taskTimerElements.size());
for (Element timerElement : taskTimerElements) {
Timer timer = parseTimerElement(timerElement, true);
timers.add(timer);
}
node.setTimers(timers);
}
protected void parseTimerActions(Element timersElement, Timer timer) {
if (timersElement == null) {
return;
}
List<Element> timerActionElements = timersElement.elements(
"timer-action");
parseActionElements(timerActionElements, timer);
List<Element> timerNotificationElements = timersElement.elements(
"timer-notification");
parseNotificationElements(timerNotificationElements, timer);
Element reassignmentsElement = timersElement.element("reassignments");
if (reassignmentsElement != null) {
Set<Assignment> assignments = parseAssignments(
reassignmentsElement);
timer.setReassignments(assignments);
}
}
protected Timer parseTimerElement(Element timerElement, boolean taskTimer) {
String name = timerElement.elementTextTrim("name");
String description = timerElement.elementTextTrim("description");
boolean blocking = GetterUtil.getBoolean(
timerElement.elementTextTrim("blocking"), !taskTimer);
Timer timer = new Timer(name, description, blocking);
Element delayElement = timerElement.element("delay");
DelayDuration delayDuration = parseDelay(delayElement);
timer.setDelayDuration(delayDuration);
if (!blocking) {
Element recurrenceElement = timerElement.element("recurrence");
DelayDuration recurrence = parseDelay(recurrenceElement);
timer.setRecurrence(recurrence);
}
Element timerActions = timerElement.element("timer-actions");
parseTimerActions(timerActions, timer);
return timer;
}
protected void parseTimerElements(Element timersElement, Node node) {
if (timersElement == null) {
return;
}
List<Element> timerElements = timersElement.elements("timer");
if (timerElements.isEmpty()) {
return;
}
Set<Timer> timers = new HashSet<>(timerElements.size());
for (Element timerElement : timerElements) {
Timer timer = parseTimerElement(timerElement, false);
timers.add(timer);
}
node.setTimers(timers);
}
protected void parseTransition(Definition definition, Element nodeElement) {
String sourceName = nodeElement.elementTextTrim("name");
Node sourceNode = definition.getNode(sourceName);
Element transitionsElement = nodeElement.element("transitions");
if (transitionsElement == null) {
return;
}
List<Element> transitionElements = transitionsElement.elements(
"transition");
for (Element transitionElement : transitionElements) {
String transitionName = transitionElement.elementTextTrim("name");
String targetName = transitionElement.elementTextTrim("target");
Node targetNode = definition.getNode(targetName);
boolean defaultValue = GetterUtil.getBoolean(
transitionElement.elementTextTrim("default"), true);
Transition transition = new Transition(
transitionName, sourceNode, targetNode, defaultValue);
Element timerElement = transitionElement.element("timer");
if (timerElement != null) {
Timer timer = parseTimerElement(timerElement, false);
transition.setTimers(timer);
}
sourceNode.addOutgoingTransition(transition);
if (Validator.isNotNull(targetNode)) {
targetNode.addIncomingTransition(transition);
}
}
}
protected void parseTransitions(
Definition definition, List<Element> conditionElements,
List<Element> forkElements, List<Element> joinElements,
List<Element> joinXorElements, List<Element> stateElements,
List<Element> taskElements) {
for (Element conditionElement : conditionElements) {
parseTransition(definition, conditionElement);
}
for (Element forkElement : forkElements) {
parseTransition(definition, forkElement);
}
for (Element joinElement : joinElements) {
parseTransition(definition, joinElement);
}
for (Element joinXorElement : joinXorElements) {
parseTransition(definition, joinXorElement);
}
for (Element stateElement : stateElements) {
parseTransition(definition, stateElement);
}
for (Element taskElement : taskElements) {
parseTransition(definition, taskElement);
}
}
private boolean _validate;
}