package is.idega.idegaweb.egov.bpm.cases.exe;
import is.idega.idegaweb.egov.application.business.ApplicationBusiness;
import is.idega.idegaweb.egov.application.data.Application;
import is.idega.idegaweb.egov.application.data.ApplicationHome;
import is.idega.idegaweb.egov.bpm.application.AppSupportsManager;
import is.idega.idegaweb.egov.bpm.application.AppSupportsManagerFactory;
import is.idega.idegaweb.egov.bpm.cases.CasesBPMProcessConstants;
import is.idega.idegaweb.egov.bpm.cases.CasesStatusMapperHandler;
import is.idega.idegaweb.egov.bpm.cases.manager.BPMCasesRetrievalManagerImpl;
import is.idega.idegaweb.egov.cases.business.CasesBusiness;
import is.idega.idegaweb.egov.cases.data.GeneralCase;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.FinderException;
import javax.servlet.ServletContext;
import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.taskmgmt.exe.TaskInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.idega.block.process.data.CaseStatus;
import com.idega.bpm.BPMConstants;
import com.idega.bpm.exe.DefaultBPMProcessDefinitionW;
import com.idega.bpm.xformsview.XFormsView;
import com.idega.business.IBOLookup;
import com.idega.business.IBOLookupException;
import com.idega.business.IBORuntimeException;
import com.idega.data.IDOLookup;
import com.idega.data.IDOLookupException;
import com.idega.idegaweb.IWApplicationContext;
import com.idega.idegaweb.IWMainApplication;
import com.idega.idegaweb.IWResourceBundle;
import com.idega.idegaweb.egov.bpm.data.CaseProcInstBind;
import com.idega.idegaweb.egov.bpm.data.CaseTypesProcDefBind;
import com.idega.idegaweb.egov.bpm.data.dao.CasesBPMDAO;
import com.idega.jbpm.JbpmCallback;
import com.idega.jbpm.exe.ProcessConstants;
import com.idega.jbpm.view.View;
import com.idega.jbpm.view.ViewSubmission;
import com.idega.presentation.IWContext;
import com.idega.presentation.PresentationObject;
import com.idega.user.business.UserBusiness;
import com.idega.user.data.User;
import com.idega.util.CoreUtil;
import com.idega.util.IWTimestamp;
import com.idega.util.ListUtil;
import com.idega.util.StringUtil;
import com.idega.util.datastructures.map.MapUtil;
/**
* @author <a href="mailto:civilis@idega.com">Vytautas Čivilis</a>
* @version $Revision: 1.52 $ Last modified: $Date: 2009/06/30 13:17:35 $ by $Author: valdas $
*/
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@Service(CasesBPMProcessDefinitionW.SPRING_BEAN_IDENTIFIER)
public class CasesBPMProcessDefinitionW extends DefaultBPMProcessDefinitionW {
public static final String SPRING_BEAN_IDENTIFIER = "casesPDW";
@Autowired
private CasesBPMDAO casesBPMDAO;
@Autowired
@Qualifier(CaseIdentifier.QUALIFIER)
private CaseIdentifier caseIdentifier;
@Autowired
private CasesStatusMapperHandler casesStatusMapperHandler;
@Autowired
private AppSupportsManagerFactory appSupportsManagerFactory;
@Transactional(readOnly = false)
@Override
public Long startProcess(final ViewSubmission viewSubmission) {
final Long processDefinitionId = viewSubmission.getProcessDefinitionId();
if (!processDefinitionId.equals(getProcessDefinitionId()))
throw new IllegalArgumentException("View submission was for different process definition id than tried to submit to");
final ProcessDefinition pd = getProcessDefinition();
final String procDefName = pd.getName();
getLogger().info("Starting process for process definition id = " + processDefinitionId + ", process definition name: " + procDefName);
Map<String, String> parameters = viewSubmission.resolveParameters();
getLogger().finer("Params " + parameters);
final Integer userId = parameters.containsKey(CasesBPMProcessConstants.userIdActionVariableName) ?
Integer.parseInt(parameters.get(CasesBPMProcessConstants.userIdActionVariableName)) : null;
final String caseStatusKey = parameters.containsKey(CasesBPMProcessConstants.caseStatusVariableName) ?
parameters.get(CasesBPMProcessConstants.caseStatusVariableName) : null;
final Integer caseIdentifierNumber = Integer.parseInt(parameters.get(CasesBPMProcessConstants.caseIdentifierNumberParam));
final String caseIdentifier = parameters.get(com.idega.block.process.business.ProcessConstants.CASE_IDENTIFIER);
final String realCaseCreationDate = parameters.get(CasesBPMProcessConstants.caseCreationDateParam);
final Date caseCreated = StringUtil.isEmpty(realCaseCreationDate) ?
new Timestamp(System.currentTimeMillis()) :
new IWTimestamp(realCaseCreationDate).getTimestamp();
final Map<String, Object> variables = new HashMap<String, Object>();
Long piId = getBpmContext().execute(new JbpmCallback() {
@Override
public Long doInJbpm(JbpmContext context) throws JbpmException {
try {
ProcessInstance pi = new ProcessInstance(pd);
TaskInstance ti = pi.getTaskMgmtInstance().createStartTaskInstance();
View view = getBpmFactory().getView(viewSubmission.getViewId(), viewSubmission.getViewType(), false);
// binding view to task instance
view.getViewToTask().bind(view, ti);
getLogger().info("New process instance created for the process " + procDefName);
pi.setStart(new Date());
IWApplicationContext iwac = getIWAC();
IWMainApplication iwma = iwac.getIWMainApplication();
UserBusiness userBusiness = getUserBusiness(iwac);
User user = userId == null ? null : userBusiness.getUser(userId);
CasesBusiness casesBusiness = getCasesBusiness(iwac);
CaseTypesProcDefBind bind = getCasesBPMDAO().find(CaseTypesProcDefBind.class, procDefName);
Long caseCategoryId = bind.getCasesCategoryId();
Long caseTypeId = bind.getCasesTypeId();
IWResourceBundle iwrb = null;
GeneralCase genCase = null;
try {
iwrb = casesBusiness.getIWResourceBundleForUser(user, null, iwma.getBundle(PresentationObject.CORE_IW_BUNDLE_IDENTIFIER));
genCase = casesBusiness.storeGeneralCase(
user,
caseCategoryId,
caseTypeId,
null,
null,
"This is simple cases-jbpm-formbuilder integration example.",
null,
BPMCasesRetrievalManagerImpl.caseHandlerType,
false,
iwrb,
false, caseIdentifier, true, caseStatusKey, new IWTimestamp(caseCreated).getTimestamp()
);
} catch (Exception e) {
String message = "Error creating case for BPM process: " + pi.getId() + ". User: " + user + ", case category ID: " +
caseCategoryId + ", case type ID: " + caseTypeId + ", resource bunlde: " + iwrb + ", case identifier: " +
caseIdentifier + ", case status key: " + caseStatusKey;
getLogger().log(Level.SEVERE, message, e);
CoreUtil.sendExceptionNotification(message, e);
throw new RuntimeException(message, e);
}
getLogger().info("Case (id=" + genCase.getPrimaryKey() + ") created for process instance " + pi.getId());
pi.setStart(caseCreated);
Map<String, Object> caseData = new HashMap<String, Object>();
caseData.put(CasesBPMProcessConstants.caseIdVariableName, genCase.getPrimaryKey().toString());
caseData.put(CasesBPMProcessConstants.caseTypeNameVariableName, genCase.getCaseType().getName());
caseData.put(CasesBPMProcessConstants.caseCategoryNameVariableName, genCase.getCaseCategory().getName());
caseData.put(CasesBPMProcessConstants.caseStatusVariableName, genCase.getCaseStatus().getStatus());
caseData.put(CasesBPMProcessConstants.caseStatusClosedVariableName, casesBusiness.getCaseStatusReady().getStatus());
caseData.put(com.idega.block.process.business.ProcessConstants.CASE_IDENTIFIER, caseIdentifier);
Collection<CaseStatus> allStatuses = casesBusiness.getCaseStatuses();
CasesStatusMapperHandler casesStatusMapper = getCasesStatusMapperHandler();
for (CaseStatus caseStatus : allStatuses)
caseData.put(casesStatusMapper.getStatusVariableNameFromStatusCode(caseStatus.getStatus()), caseStatus.getStatus());
final Locale dateLocale;
IWContext iwc = CoreUtil.getIWContext();
dateLocale = iwc == null ? userBusiness.getUsersPreferredLocale(user) : iwc.getCurrentLocale();
IWTimestamp created = new IWTimestamp(genCase.getCreated());
caseData.put(CasesBPMProcessConstants.caseCreatedDateVariableName, created.getLocaleDateAndTime(dateLocale, IWTimestamp.SHORT,
IWTimestamp.SHORT));
CaseProcInstBind piBind = new CaseProcInstBind();
piBind.setCaseId(new Integer(genCase.getPrimaryKey().toString()));
piBind.setProcInstId(pi.getId());
piBind.setCaseIdentierID(caseIdentifierNumber);
piBind.setDateCreated(caseCreated);
piBind.setCaseIdentifier(caseIdentifier);
getCasesBPMDAO().persist(piBind);
getLogger().info("Bind was created: process instance ID=" + pi.getId() + ", case ID=" + genCase.getPrimaryKey());
pi.getContextInstance().setVariables(caseData);
getLogger().info("Variables were set: " + caseData);
variables.putAll(viewSubmission.resolveVariables());
submitVariablesAndProceedProcess(ti, variables, true);
if (variables != null && variables.containsKey(BPMConstants.PUBLIC_PROCESS)) {
Object publicProcess = variables.get(BPMConstants.PUBLIC_PROCESS);
if (Boolean.valueOf(publicProcess.toString())) {
genCase.setAsAnonymous(Boolean.TRUE);
genCase.store();
}
}
getLogger().info("Variables were submitted and a process proceeded");
return pi.getId();
} catch (JbpmException e) {
throw e;
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
try {
getLogger().info("Process was created: " + piId);
return piId;
} finally {
notifyAboutNewProcess(procDefName, piId, variables);
}
}
@Override
@Transactional(readOnly = false)
public View loadInitView(final Integer initiatorId) {
try {
return getBpmContext().execute(new JbpmCallback() {
@Override
public Object doInJbpm(JbpmContext context) throws JbpmException {
Long processDefinitionId = getProcessDefinitionId();
ProcessDefinition pd = getProcessDefinition();
Long startTaskId = pd.getTaskMgmtDefinition().getStartTask().getId();
List<String> preferred = new ArrayList<String>(1);
preferred.add(XFormsView.VIEW_TYPE);
View view = getBpmFactory().getViewByTask(startTaskId, true, preferred);
view.takeView();
// we don't know yet the task instance id, so we store the
// view id
// and type, to resolve later in start process. Only then we
// will
// bind view with task instance
String caseIdentifierQualifier = IWMainApplication.getDefaultIWMainApplication()
.getSettings().getProperty("case_identifier_qualifier", CaseIdentifier.QUALIFIER);
Object[] identifiers = getCaseIdentifier(caseIdentifierQualifier).generateNewCaseIdentifier();
Integer identifierNumber = (Integer) identifiers[0];
String identifier = (String) identifiers[1];
IWTimestamp realCreationDate = new IWTimestamp();
String realCreationDateString = realCreationDate.toString();
Map<String, String> parameters = new HashMap<String, String>(7);
parameters.put(ProcessConstants.START_PROCESS, ProcessConstants.START_PROCESS);
parameters.put(ProcessConstants.PROCESS_DEFINITION_ID, String.valueOf(processDefinitionId));
parameters.put(ProcessConstants.VIEW_ID, view.getViewId());
parameters.put(ProcessConstants.VIEW_TYPE, view.getViewType());
if (initiatorId != null)
parameters.put(CasesBPMProcessConstants.userIdActionVariableName, initiatorId.toString());
parameters.put(CasesBPMProcessConstants.caseIdentifierNumberParam, String.valueOf(identifierNumber));
parameters.put(com.idega.block.process.business.ProcessConstants.CASE_IDENTIFIER, String.valueOf(identifier));
parameters.put(CasesBPMProcessConstants.caseCreationDateParam, realCreationDateString);
view.populateParameters(parameters);
HashMap<String, Object> vars = new HashMap<String, Object>(1);
vars.put(com.idega.block.process.business.ProcessConstants.CASE_IDENTIFIER, identifier);
view.populateVariables(vars);
// --
return view;
}
});
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
@Transactional(readOnly = true)
public List<String> getRolesCanStartProcess(Object context) {
final Integer applicationId = new Integer(context.toString());
AppSupportsManager appSupportsManager = getAppSupportsManagerFactory().getAppSupportsManager(applicationId, getProcessDefinition().getName());
List<String> rolesCanStartProcess = appSupportsManager.getRolesCanStartProcess();
return rolesCanStartProcess;
}
/**
* sets roles, whose users can start process (and see application).
*
* @param rolesKeys
* - idega roles keys (<b>not</b> process roles)
* @param processContext
* - some context depending implementation, e.g., roles can start process using
* applications - then context will be application id
*/
@Override
@Transactional(readOnly = false)
public void setRolesCanStartProcess(List<String> rolesKeys, Object processContext) {
if (rolesKeys == null)
rolesKeys = Collections.emptyList();
final Integer applicationId = new Integer(processContext.toString());
AppSupportsManager appSupportsManager = getAppSupportsManagerFactory().getAppSupportsManager(applicationId, getProcessDefinition().getName());
appSupportsManager.updateRolesCanStartProcess(rolesKeys);
}
protected CasesBusiness getCasesBusiness(IWApplicationContext iwac) {
try {
return IBOLookup.getServiceInstance(iwac,
CasesBusiness.class);
} catch (IBOLookupException ile) {
throw new IBORuntimeException(ile);
}
}
protected UserBusiness getUserBusiness(IWApplicationContext iwac) {
try {
return IBOLookup.getServiceInstance(iwac, UserBusiness.class);
} catch (IBOLookupException ile) {
throw new IBORuntimeException(ile);
}
}
private IWApplicationContext getIWAC() {
IWContext iwc = CoreUtil.getIWContext();
return iwc == null ? IWMainApplication.getDefaultIWApplicationContext() : iwc;
}
public CasesBPMDAO getCasesBPMDAO() {
return casesBPMDAO;
}
public void setCasesBPMDAO(CasesBPMDAO casesBPMDAO) {
this.casesBPMDAO = casesBPMDAO;
}
private CaseIdentifier getCaseIdentifier(String qualifier) {
ServletContext sc = getIWAC().getIWMainApplication().getServletContext();
@SuppressWarnings("unchecked")
Map<String, ? extends CaseIdentifier> identifierGenerators = WebApplicationContextUtils.getWebApplicationContext(sc)
.getBeansOfType(CaseIdentifier.class);
if (MapUtil.isEmpty(identifierGenerators)) {
getLogger().warning("There are no beans (type of '"+CaseIdentifier.class+"') to generate case identifier!");
return caseIdentifier;
} else if (identifierGenerators.values().size() == 1) {
return caseIdentifier;
}
if (StringUtil.isEmpty(qualifier)) {
return identifierGenerators.values().iterator().next();
}
for (CaseIdentifier identifierGenerator: identifierGenerators.values()) {
Qualifier qualifierAnnotation = identifierGenerator.getClass().getAnnotation(Qualifier.class);
if (qualifierAnnotation != null && qualifier.equals(qualifierAnnotation.value())) {
getLogger().info("Using identifier generator: " + identifierGenerator.getClass());
return identifierGenerator;
}
}
return caseIdentifier;
}
@Override
public String getProcessName(Locale locale) {
if (locale == null) {
return null;
}
return getProcessName(getProcessDefinitionId(), locale);
}
// TODO: make caching?
@Transactional(readOnly = true)
public String getProcessName(final Long processDefinitionId, final Locale locale) {
if (processDefinitionId == null) {
return null;
}
return getBpmContext().execute(new JbpmCallback() {
@Override
public Object doInJbpm(JbpmContext context) throws JbpmException {
ProcessDefinition pd = context.getGraphSession().getProcessDefinition(processDefinitionId);
try {
return getProcessDefinitionLocalizedName(pd, locale, (ApplicationHome) IDOLookup.getHome(Application.class));
} catch (IDOLookupException e) {
e.printStackTrace();
return null;
}
}
});
}
private String getProcessDefinitionLocalizedName(ProcessDefinition pd, Locale locale, ApplicationHome appHome) {
if (pd == null || locale == null || appHome == null) {
return null;
}
Collection<Application> apps = null;
try {
apps = appHome.findAllByApplicationUrl(pd.getName());
} catch (FinderException e) {
e.printStackTrace();
}
if (ListUtil.isEmpty(apps)) {
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Didn't find any application by URL: " + pd.getName() + ", returning standard name!");
return pd.getName();
}
ApplicationBusiness applicationBusiness = null;
try {
applicationBusiness = IBOLookup.getServiceInstance(IWMainApplication.getDefaultIWApplicationContext(),
ApplicationBusiness.class);
} catch (IBOLookupException e) {
e.printStackTrace();
}
if (applicationBusiness == null) {
return pd.getName();
}
return applicationBusiness.getApplicationName(apps.iterator().next(), locale);
}
// TODO: make caching?
@Transactional(readOnly = true)
public String getProcessName(String processName, Locale locale) {
ProcessDefinition pd = getBpmFactory().getBPMDAO().findLatestProcessDefinition(processName);
if (pd == null) {
return null;
}
try {
return getProcessDefinitionLocalizedName(pd, locale, (ApplicationHome) IDOLookup.getHome(Application.class));
} catch (IDOLookupException e) {
e.printStackTrace();
}
return null;
}
public CasesStatusMapperHandler getCasesStatusMapperHandler() {
return casesStatusMapperHandler;
}
AppSupportsManagerFactory getAppSupportsManagerFactory() {
return appSupportsManagerFactory;
}
}