org.imixs.marty.team.TeamPlugin Maven / Gradle / Ivy
/*******************************************************************************
* Imixs Workflow
* Copyright (C) 2001, 2011 Imixs Software Solutions GmbH,
* http://www.imixs.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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
* General Public License for more details.
*
* You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html
*
* Project:
* http://www.imixs.org
* http://java.net/projects/imixs-workflow
*
* Contributors:
* Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika - Software Developer
*******************************************************************************/
package org.imixs.marty.team;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.WorkflowContext;
import org.imixs.workflow.WorkflowKernel;
import org.imixs.workflow.engine.WorkflowService;
import org.imixs.workflow.engine.plugins.AbstractPlugin;
import org.imixs.workflow.exceptions.PluginException;
import org.imixs.workflow.exceptions.QueryException;
/**
* The Marty TeamPlugin organizes the hierarchical order of a workitem between
* processes, spaces and workitems. A WorkItem is typically assigned to a
* process and a optional to one ore more space entities. These references are
* stored in the $UniqueIDRef property of the WorkItem. In addition to the
* $UniqueIDRef property the TeamPlugin manages the properties process.ref and
* space.ref which containing only uniqueIDs of the corresponding entity type.
* The properties process.ref and space.ref can be modified by an application to
* reassign the workitem.
*
* This plug-in supports also additional workflow properties for further
* processing. The method computes the team members and the name of the assigned
* process and space.
*
* The TeamPlugin updates the following properties:
*
* - space.team
*
- space.Manager
*
- space.Assist
*
- space.Name
*
- space.ref
*
- process.Team
*
- process.Manager
*
- process.Assist
*
- process.name
*
- process.ref
*
*
* The name properties are used in security and mail plug-ins.
*
* The properties 'process.ref' and 'space.ref' are optional and can provide the
* current $uniqueIDs for referenced space or process entities. The Plug-in
* updates the $UniqueIDRef property if these properties are filled.
*
* If the workItem is a child to another workItem (ChildWorkitem) the
* information is fetched from the parent workItem.
*
* If the workflowresultmessage of the ActivityEntity contains a space or
* process reference the plug-in will update the reference in the property
* $uniqueIdRef.
*
* Example:
*
*
- ...
- ...
*
* The Plug-in should run before Access-, Application- and Mail-Plug-in.
*
* Note: the deprecated item names are still supported:
*
* - namSpaceTeam
*
- namSpaceManager
*
- namSpaceAssist
*
- namSpaceName
*
- namProcessTeam
*
- namProcessManager
*
- namProcessAssist
*
- txtSpaceName
*
- txtProcessName
*
*
* @author rsoika
* @version 2.0
*/
public class TeamPlugin extends AbstractPlugin {
public static final String INVALID_REFERENCE_ASSIGNED_BY_MODEL = "INVALID_REFERENCE_ASSIGNED_BY_MODEL";
public static final String NO_PROCESS_ASSIGNED = "NO_PROCESS_ASSIGNED";
private ItemCollection documentContext;
private static Logger logger = Logger.getLogger(TeamPlugin.class.getName());
private Map entityCache = null;
/**
* Fetch workflowService and entityService from WorkflowContext
*/
@Override
public void init(WorkflowContext actx) throws PluginException {
super.init(actx);
// init cache
entityCache = new HashMap();
}
/**
* The method updates information from the Process and Space entiy (optional)
* stored in the attribute '$uniqueIdref'
*
* - process.ref
*
- space.ref
*
- namTeam
*
- namManager
*
- namProcessTeam
*
- namProcessManager
*
- txtSpaceName
*
- txtCoreProcessName
*
* If the workitem is a child to another workitem (ChildWorkitem) the
* information is fetched from the parent workitem.
*
* If the workflowresultmessage contains a space entity reference the plugin
* will update the reference in the property $uniqueIdRef.
*
* Example:
*
*
- ...
*
**/
@SuppressWarnings({ "unchecked" })
@Override
public ItemCollection run(ItemCollection workItem, ItemCollection documentActivity) throws PluginException {
documentContext = workItem;
List oldUnqiueIdRefList = workItem.getItemValue(WorkflowService.UNIQUEIDREF);
List newUnqiueIDRefList = null;
List processRefList = null;
List spaceRefList = null;
// 0.) migrate deprecated field names!
if (!workItem.hasItem("process.ref") && workItem.hasItem("txtprocessref")) {
workItem.setItemValueUnique("process.ref", workItem.getItemValue("txtprocessref"));
}
if (!workItem.hasItem("space.ref") && workItem.hasItem("txtspaceref")) {
workItem.setItemValueUnique("space.ref", workItem.getItemValue("txtspaceref"));
}
// 1.1) if process.ref don't exists then search for process ids in
// $UnqiueIDRef
if (!workItem.hasItem("process.ref") && !oldUnqiueIdRefList.isEmpty()) {
processRefList = workItem.getItemValue("process.ref");
for (String aUniqueID : oldUnqiueIdRefList) {
if (aUniqueID == null || aUniqueID.isEmpty()) {
continue;
}
ItemCollection entity = findEntity(aUniqueID);
if (entity != null && "process".equals(entity.getItemValueString("type"))) {
// update process.ref
processRefList.add(entity.getItemValueString(WorkflowKernel.UNIQUEID));
}
}
// update process.ref
workItem.setItemValueUnique("process.ref", processRefList);
} else {
// 1.1.1) validate content of process.ref
if (workItem.hasItem("process.ref")) {
processRefList = workItem.getItemValue("process.ref");
List verifiedRefList = new ArrayList();
for (String aUniqueID : processRefList) {
if (aUniqueID == null || aUniqueID.isEmpty()) {
continue;
}
ItemCollection entity = findEntity(aUniqueID);
// if the entity was not found by id we test if we can catch
// it up by its name...
if (entity == null) {
entity = findRefByName(aUniqueID, "process");
if (entity != null) {
String aID = entity.getItemValueString(WorkflowKernel.UNIQUEID);
logger.info(
"[TeamPlugin] processRefName '" + aUniqueID + "' translated into '" + aID + "'");
// verified
aUniqueID = aID;
}
}
if (entity != null && "process".equals(entity.getItemValueString("type"))) {
// verified
verifiedRefList.add(aUniqueID);
}
}
// update process.ref
workItem.setItemValueUnique("process.ref", verifiedRefList);
}
}
// 1.2) if space.ref don't exists then search for space ids in
// $UnqiueIDRef
if (!workItem.hasItem("space.ref") && !oldUnqiueIdRefList.isEmpty()) {
spaceRefList = workItem.getItemValue("space.ref");
for (String aUniqueID : oldUnqiueIdRefList) {
ItemCollection entity = findEntity(aUniqueID);
if (entity != null && ("space".equals(entity.getType()) || "spacearchive".equals(entity.getType()))) {
// update space.ref
spaceRefList.add(entity.getItemValueString(WorkflowKernel.UNIQUEID));
}
}
// update space.ref
workItem.setItemValueUnique("space.ref", spaceRefList);
} else {
// 1.2.1) validate content of space.ref
if (workItem.hasItem("space.ref")) {
processRefList = workItem.getItemValue("space.ref");
List verifiedRefList = new ArrayList();
for (String aUniqueID : processRefList) {
ItemCollection entity = findEntity(aUniqueID);
// if the entity was not found by id we test if we can catch
// it up by its name...
if (entity == null) {
entity = findRefByName(aUniqueID, "space");
if (entity != null) {
String aID = entity.getItemValueString(WorkflowKernel.UNIQUEID);
logger.info("[TeamPlugin] spaceRefName '" + aUniqueID + "' translated into '" + aID + "'");
// verified
aUniqueID = aID;
}
}
if (entity != null
&& ("space".equals(entity.getType()) || "spacearchive".equals(entity.getType()))) {
// verified
verifiedRefList.add(aUniqueID);
}
}
// update space.ref
workItem.setItemValueUnique("space.ref", verifiedRefList);
}
}
/*
* 2.) Check the txtActivityResult for a new Project reference.
*
* Pattern:
*
* '- ...
' '- ...
'
*/
ItemCollection evalItemCollection = this.getWorkflowService().evalWorkflowResult(documentActivity, "item",
workItem);
if (evalItemCollection != null) {
String sRef = fetchRefFromActivity("process", evalItemCollection);
if (sRef != null && !sRef.isEmpty()) {
logger.fine("[TeamPlugin] Updating process reference based on model information: " + sRef);
workItem.setItemValueUnique("process.ref", sRef);
}
sRef = fetchRefFromActivity("space", evalItemCollection);
if (sRef != null && !sRef.isEmpty()) {
logger.fine("[TeamPlugin] Updating space reference based on model information: " + sRef);
workItem.setItemValueUnique("space.ref", sRef);
}
}
// 3.) now synchronize process.ref/space.ref with $UnqiueIDref
processRefList = workItem.getItemValue("process.ref");
spaceRefList = workItem.getItemValue("space.ref");
newUnqiueIDRefList = new ArrayList();
newUnqiueIDRefList.addAll(processRefList);
newUnqiueIDRefList.addAll(spaceRefList);
for (String aUniqueID : oldUnqiueIdRefList) {
ItemCollection entity = findEntity(aUniqueID);
// check if this is a deprecated process/space ref
if (entity != null && "process".equals(entity.getItemValueString("type"))
&& !processRefList.contains(aUniqueID)) {
logger.fine("remove deprecated processRef " + aUniqueID);
} else {
if (entity != null && ("space".equals(entity.getType()) || "spacearchive".equals(entity.getType()))
&& !spaceRefList.contains(aUniqueID)) {
logger.fine("remove deprecated spaceRef " + aUniqueID);
} else {
// all other types of entities will still be contained...
if (!newUnqiueIDRefList.contains(aUniqueID))
newUnqiueIDRefList.add(aUniqueID);
}
}
}
// clean old values
workItem.removeItem("space.team");
workItem.removeItem("space.manager");
workItem.removeItem("space.assist");
workItem.removeItem("space.team.label");
workItem.removeItem("space.manager.label");
workItem.removeItem("space.assist.label");
workItem.removeItem("process.team");
workItem.removeItem("process.manager");
workItem.removeItem("process.assist");
workItem.removeItem("process.team.label");
workItem.removeItem("process.manager.label");
workItem.removeItem("process.assist.label");
workItem.removeItem("process.name");
workItem.removeItem("space.name");
workItem.removeItem(WorkflowService.UNIQUEIDREF);
// 4.) finally we can now update the $UniqueIDRef property
workItem.setItemValueUnique(WorkflowService.UNIQUEIDREF, newUnqiueIDRefList);
logger.fine("Updated $UniqueIdRef: " + newUnqiueIDRefList);
// 6.) Now the team lists will be updated depending of the current
// $uniqueidref - iterate over all refs if defined
for (String aUnqiueID : newUnqiueIDRefList) {
ItemCollection entity = findEntity(aUnqiueID);
if (entity != null) {
String parentType = entity.getItemValueString("type");
// Test type property....
if ("process".equals(parentType)) {
workItem.appendItemValueUnique("process.assist", entity.getItemValue("process.assist"));
workItem.appendItemValueUnique("process.team", entity.getItemValue("process.team"));
workItem.appendItemValueUnique("process.manager", entity.getItemValue("process.manager"));
workItem.appendItemValueUnique("process.assist.label", entity.getItemValue("process.assist.label"));
workItem.appendItemValueUnique("process.team.label", entity.getItemValue("process.team.label"));
workItem.appendItemValueUnique("process.manager.label",
entity.getItemValue("process.manager.label"));
workItem.appendItemValueUnique("process.name", entity.getItemValue("name"));
}
if ("space".equals(parentType) || "spacearchive".equals(parentType)) {
workItem.appendItemValueUnique("space.assist", entity.getItemValue("space.assist"));
workItem.appendItemValueUnique("space.team", entity.getItemValue("space.team"));
workItem.appendItemValueUnique("space.manager", entity.getItemValue("space.manager"));
workItem.appendItemValueUnique("space.assist.label", entity.getItemValue("space.assist.label"));
workItem.appendItemValueUnique("space.team.label", entity.getItemValue("space.team.label"));
workItem.appendItemValueUnique("space.manager.label", entity.getItemValue("space.manager.label"));
workItem.appendItemValueUnique("space.name", entity.getItemValue("space.name"));
workItem.appendItemValueUnique("space.parent.name", entity.getItemValue("space.parent.name"));
}
}
}
// update properties
// support deprecated item names... Issue #325
workItem.replaceItemValue("namSpaceTeam", workItem.getItemValue("space.team"));
workItem.replaceItemValue("namSpaceManager", workItem.getItemValue("space.manager"));
workItem.replaceItemValue("namSpaceAssist", workItem.getItemValue("space.assist"));
workItem.replaceItemValue("namProcessTeam", workItem.getItemValue("process.team"));
workItem.replaceItemValue("namProcessManager", workItem.getItemValue("process.manager"));
workItem.replaceItemValue("namProcessAssist", workItem.getItemValue("process.assist"));
workItem.replaceItemValue("txtProcessName", workItem.getItemValue("process.name"));
workItem.replaceItemValue("txtSpaceName", workItem.getItemValue("space.name"));
workItem.replaceItemValue("txtSpaceNameNode", workItem.getItemValue("space.name"));
workItem.replaceItemValue("txtProcessRef", workItem.getItemValue("process.ref"));
workItem.replaceItemValue("txtSpaceRef", workItem.getItemValue("space.ref"));
documentContext.removeItem("space");
documentContext.removeItem("process");
return documentContext;
}
/**
* Helper method to lookup an entity in internal cache or load it from database
*
* @param id
* @return entity or null if not exits
*/
public ItemCollection findEntity(String id) {
ItemCollection entity = entityCache.get(id);
if (entity == null) {
// load entity
entity = this.getWorkflowService().getDocumentService().load(id);
if (entity == null) {
// add a dummy entry....
entity = new ItemCollection();
}
// cache entity
entityCache.put(id, entity);
}
// if entity is dummy return null
if (entity.getItemValueString(WorkflowKernel.UNIQUEID).isEmpty())
return null;
else
return entity;
}
private String fetchRefFromActivity(String type, ItemCollection evalItemCollection) throws PluginException {
String sRef = null;
// Read workflow result directly from the activity definition
String aActivityRefName = evalItemCollection.getItemValueString(type);
// 1.) check if a new space reference is defined in the current
// activity. This will overwrite the current value!!
if (!"".equals(aActivityRefName)) {
ItemCollection entity = this.getWorkflowService().getDocumentService().load(aActivityRefName);
if (entity != null && !type.equals(entity.getItemValueString("type"))) {
entity = null;
}
if (entity == null) {
// load space entity
entity = findRefByName(aActivityRefName, type);
}
if (entity != null) {
sRef = entity.getItemValueString(WorkflowKernel.UNIQUEID);
logger.fine("[TeamPlugin] found ref from Activity for: " + aActivityRefName);
} else {
// throw a PLuginException
throw new PluginException(TeamPlugin.class.getSimpleName(), INVALID_REFERENCE_ASSIGNED_BY_MODEL,
type + " '" + aActivityRefName + "' defined by the current model can not be found!");
}
}
return sRef;
}
/**
* This method returns a Process or Space entity for a specified name. Returns
* null if no entity with the provided name was found
*
* Because of the fact that spaces can be ordered in a hirachical order we need
* to be a little more tricky if we seach for spaces....
*/
private ItemCollection findRefByName(String aName, String type) {
if (type == null || aName == null) {
return null;
}
// String sQuery = "(type:\"" + type + "\" AND txtname:\"" + aName + "\")";
// String sQuery = "((type:\"" + type + "\" OR type:\"" + type + "archive\") AND
// txtname:\"" + aName + "\")";
// We need to ensure that the old deprecated item name 'txtname' is still
// supported by the query.
String sQuery = "(type:\"" + type + "\" OR type:\"" + type + "archive\") AND (name:\"" + aName
+ "\" OR txtname:\"" + aName + "\")";
// because of the fact that spaces can be ordered in a hirachical order
// we need to be a little more tricky if we seach for spaces....
// Important: to find ambigous space names we search for maxount=2!
List col;
try {
col = this.getWorkflowService().getDocumentService().find(sQuery, 2, 0);
if (col.size() == 0) {
logger.warning("findRefByName '" + aName + "' not found!");
} else {
if (col.size() > 1) {
logger.warning("findRefByName '" + aName + "' is ambiguous!");
} else {
// we found one!
ItemCollection entity = col.iterator().next();
// update cache
entityCache.put(entity.getItemValueString(WorkflowKernel.UNIQUEID), entity);
return entity;
}
}
} catch (QueryException e) {
logger.warning("findRefByName - invalid query: " + e.getMessage());
}
// no match
return null;
}
}