/******************************************************************************* * 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.plugins; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import java.util.logging.Logger; import org.imixs.workflow.ItemCollection; import org.imixs.workflow.WorkflowKernel; import org.imixs.workflow.engine.WorkflowService; import org.imixs.workflow.engine.plugins.AbstractPlugin; import org.imixs.workflow.engine.plugins.ResultPlugin; import org.imixs.workflow.exceptions.AccessDeniedException; import org.imixs.workflow.exceptions.ModelException; import org.imixs.workflow.exceptions.PluginException; import org.imixs.workflow.exceptions.ProcessingErrorException; /** * The DMSSplitPlugin provides functionality to create sub-process instances for * each attachment added in an origin process. The configuration is similar to * the Imixs-Workflow SplitAndJoinPlugin. The item name to trigger the creation * of subprocesses is "dms_subprocess_create" The tag '<remove>true</remove>' * indicates that the attachments will be removed from the origin process after * the subprocess was created. * * <code> <item name="dms_subprocess_create"> <modelversion>1.0.0</modelversion> <processid>100</processid> <activityid>10</activityid> <items>namTeam</items> </item> * </code> * * * A subprocess will contain the $UniqueID of the origin process stored in the * property $uniqueidRef. The origin process will contain a link to the * subprocess stored in the property txtworkitemRef. So both workitems are * linked together. * * The list of attachments will be taken from the BlobWorkitem. * * * @author Ralph Soika * @version 1.0 * @see http://www.imixs.org/doc/engine/plugins/splitandjoinplugin.html * */ public class DMSSplitPlugin extends AbstractPlugin { public static final String LINK_PROPERTY = "txtworkitemref"; public static final String DMS_SUBPROCESS_CREATE = "dms_subprocess_create"; private static Logger logger = Logger.getLogger(DMSSplitPlugin.class.getName()); /** * The method evaluates the workflow activity result for items with name: * * subprocess_create * * subprocess_update * * origin_update * * For each item a corresponding processing cycle will be started. * */ @SuppressWarnings("unchecked") public ItemCollection run(ItemCollection adocumentContext, ItemCollection adocumentActivity) throws PluginException { ItemCollection evalItemCollection = ResultPlugin.evaluateWorkflowResult(adocumentActivity, adocumentContext); if (evalItemCollection == null) return adocumentContext; // 1.) test for items with name dms_subprocess_create and create the // defined suprocesses if (evalItemCollection.hasItem(DMS_SUBPROCESS_CREATE)) { logger.fine("processing " + DMS_SUBPROCESS_CREATE); // extract the create subprocess definitions... List<String> processValueList = evalItemCollection.getItemValue(DMS_SUBPROCESS_CREATE); if (processValueList == null || processValueList.size() == 0) { // no definition found logger.warning("Invalid DMS_SUBPROCESS_CREATE definition found, please check model!"); return adocumentContext; } if (processValueList.size() > 1) { // no definition found logger.warning("More than one DMS_SUBPROCESS_CREATE definitions found, please check model!"); } String processValue = processValueList.get(0); if (processValue.trim().isEmpty()) { // no definition found logger.warning("Invalid DMS_SUBPROCESS_CREATE definition found, please check model!"); return adocumentContext; } // evaluate the item content (XML format expected here!) ItemCollection dmsProcessData = ResultPlugin.parseItemStructure(processValue); try { adocumentContext = createSubprocesses(dmsProcessData, adocumentContext); } catch (ModelException e) { throw new PluginException(e.getErrorContext(),e.getErrorCode(),e.getMessage(),e); } } return adocumentContext; } /** * This method expects a of DMS-Subprocess definition and creates for each * attachment a new subprocess. The reference of the created subprocess will * be stored in the property txtworkitemRef of the origin workitem. The * method will remove attachments form the originWorkitem in case the tag * <remove>true</remove> is found. * * The method returns the originWorkitem * * @param processData * - DMSsubProcessDefinition * @param originWorkitem * @return originWorkitem * @throws AccessDeniedException * @throws ProcessingErrorException * @throws PluginException */ private ItemCollection createSubprocesses(final ItemCollection processData, final ItemCollection originWorkitem) throws AccessDeniedException, ProcessingErrorException, PluginException, ModelException { if (processData != null) { // get all attachments Map<String, List<Object>> files = loadFilesFromBlobWorkitem(originWorkitem); if (files == null) { return originWorkitem; } for (Map.Entry<String, List<Object>> entry : files.entrySet()) { String key = entry.getKey(); List<Object> value = entry.getValue(); logger.fine("create dms_subprocess for " + key); // create new process instance for each attachment ItemCollection workitemSubProcess = new ItemCollection(); // now clone the field list... copyItemList(processData.getItemValueString("items"), originWorkitem, workitemSubProcess); workitemSubProcess.replaceItemValue(WorkflowKernel.MODELVERSION, processData.getItemValueString("modelversion")); workitemSubProcess.replaceItemValue(WorkflowKernel.PROCESSID, new Integer(processData.getItemValueString("processid"))); workitemSubProcess.replaceItemValue(WorkflowKernel.ACTIVITYID, new Integer(processData.getItemValueString("activityid"))); // add the origin reference workitemSubProcess.replaceItemValue(WorkflowService.UNIQUEIDREF, originWorkitem.getUniqueID()); // add the attachment byte[] bytes = (byte[]) value.get(1); workitemSubProcess.addFile(bytes, key, value.get(0).toString()); // process the new subprocess... workitemSubProcess = getWorkflowService().processWorkItem(workitemSubProcess); logger.fine("successful created new subprocess."); // finally add the new workitemRef into the origin // documentContext addWorkitemRef(workitemSubProcess.getUniqueID(), originWorkitem); } // remove attachments? if (processData.getItemValueBoolean("remove")) { for (String filename : files.keySet()) { logger.fine("remove attachment '" + filename + "'"); originWorkitem.removeFile(filename); } } } return originWorkitem; } /** * This Method copies the fields defined in 'items' into the targetWorkitem. * Multiple values are separated with comma ','. * * In case a item name contains '|' the target field name will become the * right part of the item name. */ private void copyItemList(String items, ItemCollection source, ItemCollection target) { // clone the field list... StringTokenizer st = new StringTokenizer(items, ","); while (st.hasMoreTokens()) { String field = st.nextToken().trim(); int pos = field.indexOf('|'); if (pos > -1) { target.replaceItemValue(field.substring(pos + 1).trim(), source.getItemValue(field.substring(0, pos).trim())); } else { target.replaceItemValue(field, source.getItemValue(field)); } } } /** * This methods adds a new workItem reference into a workitem */ private void addWorkitemRef(String aUniqueID, ItemCollection workitem) { logger.fine("LinkController add workitem reference: " + aUniqueID); @SuppressWarnings("unchecked") List<String> refList = workitem.getItemValue(LINK_PROPERTY); // clear empty entry if set if (refList.size() == 1 && "".equals(refList.get(0))) refList.remove(0); // test if not yet a member of if (refList.indexOf(aUniqueID) == -1) { refList.add(aUniqueID); workitem.replaceItemValue(LINK_PROPERTY, refList); } } /** * This method loads the BlobWorkitem for a given parent WorkItem and * returns the FileList. The BlobWorkitem is identified by the $unqiueidRef. * * If no BlobWorkitem still exists the method returns null * */ private Map<String, List<Object>> loadFilesFromBlobWorkitem(ItemCollection parentWorkitem) { ItemCollection blobWorkitem = null; // is parentWorkitem defined? if (parentWorkitem == null) return null; blobWorkitem = BlobWorkitemHandler.load(this.getWorkflowService().getDocumentService(), parentWorkitem); // if blobWorkitem was found, return the file list. if (blobWorkitem != null) { Map<String, List<Object>> files = blobWorkitem.getFiles(); return files; } return null; } }