/*******************************************************************************
* 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.logging.Logger;
import javax.mail.internet.AddressException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.imixs.marty.ejb.SequenceService;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.WorkflowContext;
import org.imixs.workflow.engine.plugins.AbstractPlugin;
import org.imixs.workflow.exceptions.AccessDeniedException;
import org.imixs.workflow.exceptions.PluginException;
/**
* This Plugin handles a unique sequence number for all workItems. The current
* sequenceNumber for a workitem is based on the workflowGroup. The next free
* sequence number will be stored in the configuration Entity 'BASIC'. The
* configuration entity provides a property named 'sequencenumbers' with the
* current number range for each workflowGroup.
*
* If a WorkItem is assigned to a WorkflowGroup with no corresponding entry in
* the BASIC configuration, the Plugin will not compute a new number for the
* workitem.
*
* If the Workitem still have a sequence number stored the plugin will not run.
* The new computed SequenceNumer will be stored into the property
* 'numsequencenumber'. To compute the sequence Number the plugin uses the
* stateless session EJB SequeceService which updates the latest used sequence
* Number.
*
* --- Optimistic Locking---------
* <p>
* If the WorkItem is a ChildWorkitem (type=childworkitem) then the mechanism is
* stopped. In earlier version the sequcenceNumber was computed based on the
* LastSequenceNumber stored in the parentWorkitem. In this case the parent
* Workitem was the configuration entity for the next sequenceNumber. But
* because of Optmistic locking this leads into a exception in the frontend
* (because of the fact, that the frontend controller holds a local copy of the
* parent workitem) This is the reason why the mechanism is disabled here!
* <p>
* --- Solution-------------------
* <p>
* The 'Optimistic Locking' problem could be solved if the client removes the
* property '$version' from the parent workitem.
*
*
* @see SequenceService
* @author rsoika
* @version 1.0
*
*/
public class SequenceNumberPlugin extends AbstractPlugin {
SequenceService sequenceService = null;
long sequenceNumber = -1;
ItemCollection workitem = null;
private static Logger logger = Logger.getLogger(SequenceNumberPlugin.class.getName());
public static String NO_SEQUENCE_SERVICE_FOUND = "NO_SEQUENCE_SERVICE_FOUND";
public void init(WorkflowContext actx) throws PluginException {
super.init(actx);
// lookup profile service EJB
String jndiName = "ejb/SequenceService";
InitialContext ictx;
try {
ictx = new InitialContext();
Context ctx = (Context) ictx.lookup("java:comp/env");
sequenceService = (SequenceService) ctx.lookup(jndiName);
} catch (NamingException e) {
throw new PluginException(SequenceNumberPlugin.class.getSimpleName(), NO_SEQUENCE_SERVICE_FOUND,
"[SequenceNumberPlugin] unable to lookup sequenceService: ", e);
}
}
/**
* This method loads a sequence number object and increases the sequence
* number in the workItem. If the workItem is form type'workitem' then the
* next sequecnenumer is computed based on the workflowGroup. If the
* workItem is from type='childworkitem' then the next sequencenumber is
* computed on the parent workItem.
*
* @return
* @throws AddressException
*/
@Override
public ItemCollection run(ItemCollection documentContext, ItemCollection documentActivity) throws PluginException {
workitem = documentContext;
// check type
/*
* The plugin will only run for type='worktiem'. Childworkitems are no
* longer supported as a optimistic locking exception will be forced in
* the frontend (because of updated version id)
*
* || "childworkitem".equals(sType)
*/
String sType = workitem.getItemValueString("Type");
// also give a squence number for archived workitems
if (!sType.startsWith("workitem") || sType.endsWith("deleted"))
return workitem;
/* check if worktitem still have a sequence number? */
if (workitem.getItemValueInteger("numsequencenumber") == 0) {
logger.fine(
"SequenceNumberPlugin calculating next sequencenumber: '" + documentContext.getUniqueID() + "'");
try {
// load next Number based on the type of workitem
if (sType.startsWith("workitem"))
sequenceNumber = sequenceService.getNextSequenceNumberByGroup(documentContext);
else
sequenceNumber = sequenceService.getNextSequenceNumberByParent(documentContext);
} catch (AccessDeniedException e) {
throw new PluginException(e.getErrorContext(), e.getErrorCode(), "[SequenceNumberPlugin] error ", e);
}
if (sequenceNumber > 0)
workitem.replaceItemValue("numsequencenumber", new Long(sequenceNumber));
else {
// to avoid problems with incorrect data values we remove the
// property numsequencenumber in this case
workitem.removeItem("numsequencenumber");
}
}
return workitem;
}
}