Java tutorial
/** * Copyright 2005-2014 The Kuali Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ecl2.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kuali.rice.kew.rule.service.impl; import java.io.InputStream; import java.sql.Timestamp; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.StringUtils; import org.jdom.Element; import org.kuali.rice.core.api.CoreApiServiceLocator; import org.kuali.rice.core.api.impex.ExportDataSet; import org.kuali.rice.core.api.impex.xml.XmlConstants; import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader; import org.kuali.rice.core.api.util.RiceConstants; import org.kuali.rice.core.api.util.collect.CollectionUtils; import org.kuali.rice.core.impl.cache.DistributedCacheManagerDecorator; import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator; import org.kuali.rice.kew.actionrequest.service.ActionRequestService; import org.kuali.rice.kew.api.KewApiConstants; import org.kuali.rice.kew.api.KewApiServiceLocator; import org.kuali.rice.kew.api.WorkflowDocument; import org.kuali.rice.kew.api.WorkflowDocumentFactory; import org.kuali.rice.kew.api.WorkflowRuntimeException; import org.kuali.rice.kew.api.action.ActionRequestPolicy; import org.kuali.rice.kew.api.rule.Rule; import org.kuali.rice.kew.api.rule.RuleDelegation; import org.kuali.rice.kew.api.rule.RuleExtension; import org.kuali.rice.kew.api.rule.RuleResponsibility; import org.kuali.rice.kew.api.validation.RuleValidationContext; import org.kuali.rice.kew.api.validation.ValidationResults; import org.kuali.rice.kew.doctype.bo.DocumentType; import org.kuali.rice.kew.doctype.service.DocumentTypeService; import org.kuali.rice.kew.exception.WorkflowServiceErrorException; import org.kuali.rice.kew.exception.WorkflowServiceErrorImpl; import org.kuali.rice.kew.impl.KewImplConstants; import org.kuali.rice.kew.responsibility.service.ResponsibilityIdService; import org.kuali.rice.kew.routeheader.DocumentRouteHeaderValue; import org.kuali.rice.kew.routeheader.service.RouteHeaderService; import org.kuali.rice.kew.rule.RuleBaseValues; import org.kuali.rice.kew.rule.RuleDelegationBo; import org.kuali.rice.kew.rule.RuleExtensionBo; import org.kuali.rice.kew.rule.RuleExtensionValue; import org.kuali.rice.kew.rule.RuleResponsibilityBo; import org.kuali.rice.kew.rule.RuleRoutingDefinition; import org.kuali.rice.kew.rule.RuleValidationAttribute; import org.kuali.rice.kew.rule.bo.RuleTemplateAttributeBo; import org.kuali.rice.kew.rule.bo.RuleTemplateBo; import org.kuali.rice.kew.rule.dao.RuleDAO; import org.kuali.rice.kew.rule.dao.RuleResponsibilityDAO; import org.kuali.rice.kew.rule.service.RuleDelegationService; import org.kuali.rice.kew.rule.service.RuleServiceInternal; import org.kuali.rice.kew.rule.service.RuleTemplateService; import org.kuali.rice.kew.service.KEWServiceLocator; import org.kuali.rice.kew.util.PerformanceLogger; import org.kuali.rice.kew.xml.RuleXmlParser; import org.kuali.rice.kew.xml.export.RuleXmlExporter; import org.kuali.rice.kim.api.group.Group; import org.kuali.rice.kim.api.group.GroupService; import org.kuali.rice.kim.api.identity.principal.PrincipalContract; import org.kuali.rice.kim.api.services.KimApiServiceLocator; import org.kuali.rice.krad.UserSession; import org.kuali.rice.krad.data.DataObjectService; import org.kuali.rice.krad.util.GlobalVariables; import org.kuali.rice.krad.util.KRADConstants; import org.springframework.beans.factory.annotation.Required; import org.springframework.cache.Cache; @SuppressWarnings("deprecation") public class RuleServiceInternalImpl implements RuleServiceInternal { private static final String XML_PARSE_ERROR = "general.error.parsexml"; private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger .getLogger(RuleServiceInternalImpl.class); private RuleDAO ruleDAO; private RuleResponsibilityDAO ruleResponsibilityDAO; private DataObjectService dataObjectService; public RuleResponsibilityDAO getRuleResponsibilityDAO() { return ruleResponsibilityDAO; } @Override public RuleBaseValues getRuleByName(String name) { return ruleDAO.findRuleBaseValuesByName(name); } @Override public RuleBaseValues findDefaultRuleByRuleTemplateId(String ruleTemplateId) { return this.ruleDAO.findDefaultRuleByRuleTemplateId(ruleTemplateId); } public void setRuleResponsibilityDAO(RuleResponsibilityDAO ruleResponsibilityDAO) { this.ruleResponsibilityDAO = ruleResponsibilityDAO; } @Override public void save2(RuleBaseValues ruleBaseValues) throws Exception { save2(ruleBaseValues, null, true); } public void save2(RuleBaseValues ruleBaseValues, RuleDelegationBo ruleDelegation, boolean saveDelegations) throws Exception { if (ruleBaseValues.getPreviousRuleId() != null) { RuleBaseValues oldRule = findRuleBaseValuesById(ruleBaseValues.getPreviousRuleId()); ruleBaseValues.setPreviousVersion(oldRule); ruleBaseValues.setCurrentInd(Boolean.FALSE); ruleBaseValues.setVersionNbr(getNextVersionNumber(oldRule)); } if (ruleBaseValues.getVersionNbr() == null) { ruleBaseValues.setVersionNbr(Integer.valueOf(0)); } if (ruleBaseValues.getCurrentInd() == null) { ruleBaseValues.setCurrentInd(Boolean.FALSE); } // iterate through all associated responsibilities, and if they are unsaved (responsibilityId is null) // set a new id on them, and recursively save any associated delegation rules for (Object element : ruleBaseValues.getRuleResponsibilities()) { RuleResponsibilityBo responsibility = (RuleResponsibilityBo) element; if (responsibility.getResponsibilityId() == null) { responsibility.setResponsibilityId(getResponsibilityIdService().getNewResponsibilityId()); } if (saveDelegations) { for (Object element2 : responsibility.getDelegationRules()) { RuleDelegationBo localRuleDelegation = (RuleDelegationBo) element2; save2(localRuleDelegation.getDelegationRule(), localRuleDelegation, true); } } } validate2(ruleBaseValues, ruleDelegation, null); getRuleDAO().save(ruleBaseValues); } @Override public void makeCurrent(String documentId) { makeCurrent(findByDocumentId(documentId)); } @SuppressWarnings("unchecked") public void makeCurrent(List<RuleBaseValues> rules) { PerformanceLogger performanceLogger = new PerformanceLogger(); boolean isGenerateRuleArs = true; String generateRuleArs = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString( KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.RULE_DETAIL_TYPE, KewApiConstants.RULE_GENERATE_ACTION_REQESTS_IND); if (!StringUtils.isBlank(generateRuleArs)) { isGenerateRuleArs = KewApiConstants.YES_RULE_CHANGE_AR_GENERATION_VALUE .equalsIgnoreCase(generateRuleArs); } Set<String> responsibilityIds = new HashSet<String>(); Map<String, RuleBaseValues> rulesToSave = new HashMap<String, RuleBaseValues>(); Collections.sort(rules, new RuleDelegationSorter()); boolean delegateFirst = false; for (RuleBaseValues rule : rules) { performanceLogger.log("Preparing rule: " + rule.getDescription()); rule.setCurrentInd(Boolean.TRUE); Timestamp date = new Timestamp(System.currentTimeMillis()); rule.setActivationDate(date); try { rule.setDeactivationDate( new Timestamp(RiceConstants.getDefaultDateFormat().parse("01/01/2100").getTime())); } catch (Exception e) { LOG.error("Parse Exception", e); } rulesToSave.put(rule.getId(), rule); RuleBaseValues oldRule = rule.getPreviousVersion(); if (oldRule != null) { performanceLogger.log("Setting previous rule: " + oldRule.getId() + " to non current."); oldRule.setCurrentInd(Boolean.FALSE); oldRule.setDeactivationDate(date); rulesToSave.put(oldRule.getId(), oldRule); if (!delegateFirst) { responsibilityIds.addAll(getResponsibilityIdsFromGraph(oldRule, isGenerateRuleArs)); } //TODO if more than one delegate is edited from the create delegation screen (which currently can not happen), then this logic will not work. if (rule.getDelegateRule().booleanValue() && rule.getPreviousRuleId() != null) { delegateFirst = true; } List<RuleBaseValues> oldDelegationRules = findOldDelegationRules(oldRule, rule, performanceLogger); for (RuleBaseValues delegationRule : oldDelegationRules) { performanceLogger .log("Setting previous delegation rule: " + delegationRule.getId() + "to non current."); delegationRule.setCurrentInd(Boolean.FALSE); rulesToSave.put(delegationRule.getId(), delegationRule); responsibilityIds.addAll(getResponsibilityIdsFromGraph(delegationRule, isGenerateRuleArs)); } } for (Object element : rule.getRuleResponsibilities()) { RuleResponsibilityBo responsibility = (RuleResponsibilityBo) element; for (Object element2 : responsibility.getDelegationRules()) { RuleDelegationBo delegation = (RuleDelegationBo) element2; delegation.getDelegationRule().setCurrentInd(Boolean.TRUE); RuleBaseValues delegatorRule = delegation.getDelegationRule(); performanceLogger .log("Setting delegate rule: " + delegatorRule.getDescription() + " to current."); if (delegatorRule.getActivationDate() == null) { delegatorRule.setActivationDate(date); } try { delegatorRule.setDeactivationDate( new Timestamp(RiceConstants.getDefaultDateFormat().parse("01/01/2100").getTime())); } catch (Exception e) { LOG.error("Parse Exception", e); } rulesToSave.put(delegatorRule.getId(), delegatorRule); } } } for (RuleBaseValues rule : rulesToSave.values()) { getRuleDAO().save(rule); performanceLogger.log("Saved rule: " + rule.getId()); } getActionRequestService().updateActionRequestsForResponsibilityChange(responsibilityIds); performanceLogger.log("Time to make current"); } /** * TODO consolidate this method with makeCurrent. The reason there's a seperate implementation is because the * original makeCurrent(...) could not properly handle versioning a List of multiple rules (including multiple * delegates rules for a single parent. ALso, this work is being done for a patch so we want to mitigate the * impact on the existing rule code. * * <p>This version will only work for remove/replace operations where rules * aren't being added or removed. This is why it doesn't perform some of the functions like checking * for delegation rules that were removed from a parent rule. */ @SuppressWarnings("unchecked") public void makeCurrent2(List<RuleBaseValues> rules) { PerformanceLogger performanceLogger = new PerformanceLogger(); boolean isGenerateRuleArs = true; String generateRuleArs = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString( KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.RULE_DETAIL_TYPE, KewApiConstants.RULE_GENERATE_ACTION_REQESTS_IND); if (!StringUtils.isBlank(generateRuleArs)) { isGenerateRuleArs = KewApiConstants.YES_RULE_CHANGE_AR_GENERATION_VALUE .equalsIgnoreCase(generateRuleArs); } Set<String> responsibilityIds = new HashSet<String>(); Map<String, RuleBaseValues> rulesToSave = new HashMap<String, RuleBaseValues>(); Collections.sort(rules, new RuleDelegationSorter()); for (RuleBaseValues rule : rules) { performanceLogger.log("Preparing rule: " + rule.getDescription()); rule.setCurrentInd(Boolean.TRUE); Timestamp date = new Timestamp(System.currentTimeMillis()); rule.setActivationDate(date); try { rule.setDeactivationDate( new Timestamp(RiceConstants.getDefaultDateFormat().parse("01/01/2100").getTime())); } catch (Exception e) { LOG.error("Parse Exception", e); } rulesToSave.put(rule.getId(), rule); RuleBaseValues oldRule = rule.getPreviousVersion(); if (oldRule != null) { performanceLogger.log("Setting previous rule: " + oldRule.getId() + " to non current."); oldRule.setCurrentInd(Boolean.FALSE); oldRule.setDeactivationDate(date); rulesToSave.put(oldRule.getId(), oldRule); responsibilityIds.addAll(getModifiedResponsibilityIds(oldRule, rule)); } for (Object element : rule.getRuleResponsibilities()) { RuleResponsibilityBo responsibility = (RuleResponsibilityBo) element; for (Object element2 : responsibility.getDelegationRules()) { RuleDelegationBo delegation = (RuleDelegationBo) element2; RuleBaseValues delegateRule = delegation.getDelegationRule(); delegateRule.setCurrentInd(Boolean.TRUE); performanceLogger .log("Setting delegate rule: " + delegateRule.getDescription() + " to current."); if (delegateRule.getActivationDate() == null) { delegateRule.setActivationDate(date); } try { delegateRule.setDeactivationDate( new Timestamp(RiceConstants.getDefaultDateFormat().parse("01/01/2100").getTime())); } catch (Exception e) { LOG.error("Parse Exception", e); } rulesToSave.put(delegateRule.getId(), delegateRule); } } } for (RuleBaseValues rule : rulesToSave.values()) { getRuleDAO().save(rule); performanceLogger.log("Saved rule: " + rule.getId()); } if (isGenerateRuleArs) { getActionRequestService().updateActionRequestsForResponsibilityChange(responsibilityIds); } performanceLogger.log("Time to make current"); } /** * makeCurrent(RuleBaseValues) is the version of makeCurrent which is initiated from the new Routing Rule * Maintenance document. Because of the changes in the data model and the front end here, * this method can be much less complicated than the previous 2! */ @Override public void makeCurrent(RuleBaseValues rule, boolean isRetroactiveUpdatePermitted) { makeCurrent(null, rule, isRetroactiveUpdatePermitted); } @Override public void makeCurrent(RuleDelegationBo ruleDelegation, boolean isRetroactiveUpdatePermitted) { clearCache(RuleDelegation.Cache.NAME); makeCurrent(ruleDelegation, ruleDelegation.getDelegationRule(), isRetroactiveUpdatePermitted); } protected void makeCurrent(RuleDelegationBo ruleDelegation, RuleBaseValues rule, boolean isRetroactiveUpdatePermitted) { PerformanceLogger performanceLogger = new PerformanceLogger(); boolean isGenerateRuleArs = false; if (isRetroactiveUpdatePermitted) { isGenerateRuleArs = true; String generateRuleArs = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString( KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.RULE_DETAIL_TYPE, KewApiConstants.RULE_GENERATE_ACTION_REQESTS_IND); if (!StringUtils.isBlank(generateRuleArs)) { isGenerateRuleArs = KewApiConstants.YES_RULE_CHANGE_AR_GENERATION_VALUE .equalsIgnoreCase(generateRuleArs); } } Set<String> responsibilityIds = new HashSet<String>(); performanceLogger.log("Preparing rule: " + rule.getDescription()); generateRuleNameIfNeeded(rule); assignResponsibilityIds(rule); rule.setCurrentInd(Boolean.TRUE); Timestamp date = CoreApiServiceLocator.getDateTimeService().getCurrentTimestamp(); rule.setActivationDate(date); rule.setDeactivationDate(null); rule.setVersionNumber(null); rule.setObjectId(null); RuleBaseValues oldRule = null; if (rule.getPreviousRuleId() != null) { oldRule = findRuleBaseValuesById(rule.getPreviousRuleId()); } if (oldRule != null) { performanceLogger.log("Setting previous rule: " + oldRule.getId() + " to non current."); oldRule.setCurrentInd(Boolean.FALSE); oldRule.setDeactivationDate(date); responsibilityIds.addAll(getModifiedResponsibilityIds(oldRule, rule)); rule.setVersionNbr(getNextVersionNumber(oldRule)); oldRule = getRuleDAO().save(oldRule); rule.setPreviousVersion(oldRule); performanceLogger.log("Saved old rule: " + oldRule.getId()); } // } for (RuleResponsibilityBo ruleResponsibilityBo : rule.getRuleResponsibilities()) { if (StringUtils.isBlank(ruleResponsibilityBo.getId())) { ruleResponsibilityBo.setVersionNumber(null); } ruleResponsibilityBo.setRuleBaseValues(rule); } // now save the new rule rule = getRuleDAO().save(rule); performanceLogger.log("Saved rule: " + rule.getId()); boolean isRuleDelegation = ruleDelegation != null; if (isRuleDelegation) { // update our reference to the delegation rule, because it could have changed ruleDelegation.setDelegationRule(rule); responsibilityIds.add(ruleDelegation.getResponsibilityId()); ruleDelegation.setDelegateRuleId(rule.getId()); getRuleDelegationService().save(ruleDelegation); } if (isGenerateRuleArs && org.apache.commons.collections.CollectionUtils.isNotEmpty(responsibilityIds)) { getActionRequestService().updateActionRequestsForResponsibilityChange(responsibilityIds); } performanceLogger.log("Time to make current"); } private void clearCache(String cacheName) { DistributedCacheManagerDecorator distributedCacheManagerDecorator = GlobalResourceLoader .getService(KewImplConstants.KEW_DISTRIBUTED_CACHE_MANAGER); Cache cache = distributedCacheManagerDecorator.getCache(cacheName); if (cache != null) { cache.clear(); } } @Override public RuleBaseValues getParentRule(String ruleBaseValuesId) { return getRuleDAO().getParentRule(ruleBaseValuesId); } @SuppressWarnings({ "rawtypes", "unchecked" }) private Set getResponsibilityIdsFromGraph(RuleBaseValues rule, boolean isRuleCollecting) { Set responsibilityIds = new HashSet(); for (Object element : rule.getRuleResponsibilities()) { RuleResponsibilityBo responsibility = (RuleResponsibilityBo) element; if (isRuleCollecting) { responsibilityIds.add(responsibility.getResponsibilityId()); } } return responsibilityIds; } /** * Returns the responsibility IDs that were modified between the 2 given versions of the rule. Any added * or removed responsibilities are also included in the returned Set. */ private Set<String> getModifiedResponsibilityIds(RuleBaseValues oldRule, RuleBaseValues newRule) { Map<String, RuleResponsibilityBo> modifiedResponsibilityMap = new HashMap<String, RuleResponsibilityBo>(); for (Object element : oldRule.getRuleResponsibilities()) { RuleResponsibilityBo responsibility = (RuleResponsibilityBo) element; modifiedResponsibilityMap.put(responsibility.getResponsibilityId(), responsibility); } for (Object element : newRule.getRuleResponsibilities()) { RuleResponsibilityBo responsibility = (RuleResponsibilityBo) element; RuleResponsibilityBo oldResponsibility = modifiedResponsibilityMap .get(responsibility.getResponsibilityId()); if (oldResponsibility == null) { // if there's no old responsibility then it's a new responsibility, add it modifiedResponsibilityMap.put(responsibility.getResponsibilityId(), responsibility); } else if (!hasResponsibilityChanged(oldResponsibility, responsibility)) { // if it hasn't been modified, remove it from the collection of modified ids modifiedResponsibilityMap.remove(responsibility.getResponsibilityId()); } } return modifiedResponsibilityMap.keySet(); } /** * Determines if the given responsibilities are different or not. */ private boolean hasResponsibilityChanged(RuleResponsibilityBo oldResponsibility, RuleResponsibilityBo newResponsibility) { return !ObjectUtils.equals(oldResponsibility.getActionRequestedCd(), newResponsibility.getActionRequestedCd()) || !ObjectUtils.equals(oldResponsibility.getApprovePolicy(), newResponsibility.getActionRequestedCd()) || !ObjectUtils.equals(oldResponsibility.getPriority(), newResponsibility.getPriority()) || !ObjectUtils.equals(oldResponsibility.getRole(), newResponsibility.getRole()) || !ObjectUtils.equals(oldResponsibility.getRuleResponsibilityName(), newResponsibility.getRuleResponsibilityName()) || !ObjectUtils.equals(oldResponsibility.getRuleResponsibilityType(), newResponsibility.getRuleResponsibilityType()); } /** * This method will find any old delegation rules on the previous version of the parent rule which are not on the * new version of the rule so that they can be marked non-current. */ @SuppressWarnings("unchecked") private List<RuleBaseValues> findOldDelegationRules(RuleBaseValues oldRule, RuleBaseValues newRule, PerformanceLogger performanceLogger) { performanceLogger.log("Begin to get delegation rules."); List<RuleBaseValues> oldDelegations = getRuleDAO().findOldDelegations(oldRule, newRule); performanceLogger.log("Located " + oldDelegations.size() + " old delegation rules."); return oldDelegations; } @Override public String routeRuleWithDelegate(String documentId, RuleBaseValues parentRule, RuleBaseValues delegateRule, PrincipalContract principal, String annotation, boolean blanketApprove) throws Exception { if (parentRule == null) { throw new IllegalArgumentException("Cannot route a delegate without a parent rule."); } if (parentRule.getDelegateRule().booleanValue()) { throw new IllegalArgumentException("Parent rule cannot be a delegate."); } if (parentRule.getPreviousRuleId() == null && delegateRule.getPreviousRuleId() == null) { throw new IllegalArgumentException("Previous rule version required."); } // if the parent rule is new, unsaved, then save it // boolean isRoutingParent = parentRule.getId() == null; // if (isRoutingParent) { // // it's very important that we do not save delegations here (that's what the false parameter is for) // // this is because, if we save the delegations, the existing delegations on our parent rule will become // // saved as "non current" before the rule is approved!!! // save2(parentRule, null, false); // //save2(parentRule, null, true); // } // XXX: added when the RuleValidation stuff was added, basically we just need to get the RuleDelegation // that points to our delegate rule, this rule code is scary... RuleDelegationBo ruleDelegation = getRuleDelegation(parentRule, delegateRule); save2(delegateRule, ruleDelegation, true); // if the parent rule is new, unsaved, then save it // It's important to save the parent rule after the delegate rule is saved, that way we can ensure that any new rule // delegations have a valid, saved, delegation rule to point to (otherwise we end up with a null constraint violation) boolean isRoutingParent = parentRule.getId() == null; if (isRoutingParent) { // it's very important that we do not save delegations here (that's what the false parameter is for) // this is because, if we save the delegations, the existing delegations on our parent rule will become // saved as "non current" before the rule is approved!!! save2(parentRule, null, false); //save2(parentRule, null, true); } WorkflowDocument workflowDocument = null; if (documentId != null) { workflowDocument = WorkflowDocumentFactory.loadDocument(principal.getPrincipalId(), documentId); } else { List<RuleBaseValues> rules = new ArrayList<RuleBaseValues>(); rules.add(delegateRule); rules.add(parentRule); workflowDocument = WorkflowDocumentFactory.createDocument(principal.getPrincipalId(), getRuleDocumentTypeName(rules)); } workflowDocument.setTitle(generateTitle(parentRule, delegateRule)); delegateRule.setDocumentId(workflowDocument.getDocumentId()); workflowDocument.addAttributeDefinition( RuleRoutingDefinition.createAttributeDefinition(parentRule.getDocTypeName())); getRuleDAO().save(delegateRule); if (isRoutingParent) { parentRule.setDocumentId(workflowDocument.getDocumentId()); getRuleDAO().save(parentRule); } if (blanketApprove) { workflowDocument.blanketApprove(annotation); } else { workflowDocument.route(annotation); } return workflowDocument.getDocumentId(); } /** * Gets the RuleDelegation object from the parentRule that points to the delegateRule. */ private RuleDelegationBo getRuleDelegation(RuleBaseValues parentRule, RuleBaseValues delegateRule) throws Exception { for (Object element : parentRule.getRuleResponsibilities()) { RuleResponsibilityBo responsibility = (RuleResponsibilityBo) element; for (Object element2 : responsibility.getDelegationRules()) { RuleDelegationBo ruleDelegation = (RuleDelegationBo) element2; // they should be the same object in memory if (ruleDelegation.getDelegationRule().equals(delegateRule)) { return ruleDelegation; } } } return null; } private String generateTitle(RuleBaseValues parentRule, RuleBaseValues delegateRule) { StringBuffer title = new StringBuffer(); if (delegateRule.getPreviousRuleId() != null) { title.append("Editing Delegation Rule '").append(delegateRule.getDescription()).append("' on '"); } else { title.append("Adding Delegation Rule '").append(delegateRule.getDescription()).append("' to '"); } title.append(parentRule.getDescription()).append("'"); return title.toString(); } @SuppressWarnings({ "unchecked", "rawtypes" }) public void validate(RuleBaseValues ruleBaseValues, List errors) { if (errors == null) { errors = new ArrayList(); } if (getDocumentTypeService().findByName(ruleBaseValues.getDocTypeName()) == null) { errors.add(new WorkflowServiceErrorImpl("Document Type Invalid", "doctype.documenttypeservice.doctypename.required")); } if (ruleBaseValues.getToDateValue().before(ruleBaseValues.getFromDateValue())) { errors.add(new WorkflowServiceErrorImpl("From Date is later than to date", "routetemplate.ruleservice.daterange.fromafterto")); } if (ruleBaseValues.getDescription() == null || ruleBaseValues.getDescription().equals("")) { errors.add(new WorkflowServiceErrorImpl("Description is required", "routetemplate.ruleservice.description.required")); } if (ruleBaseValues.getRuleResponsibilities().isEmpty()) { errors.add(new WorkflowServiceErrorImpl("A responsibility is required", "routetemplate.ruleservice.responsibility.required")); } else { for (Object element : ruleBaseValues.getRuleResponsibilities()) { RuleResponsibilityBo responsibility = (RuleResponsibilityBo) element; if (responsibility.getRuleResponsibilityName() != null && KewApiConstants.RULE_RESPONSIBILITY_GROUP_ID .equals(responsibility.getRuleResponsibilityType())) { if (getGroupService().getGroup(responsibility.getRuleResponsibilityName()) == null) { errors.add(new WorkflowServiceErrorImpl("Workgroup is invalid", "routetemplate.ruleservice.workgroup.invalid")); } } else if (responsibility.getPrincipal() == null && responsibility.getRole() == null) { errors.add(new WorkflowServiceErrorImpl("User is invalid", "routetemplate.ruleservice.user.invalid")); } } } if (!errors.isEmpty()) { throw new WorkflowServiceErrorException("RuleBaseValues validation errors", errors); } } @SuppressWarnings("unchecked") @Override public void validate2(RuleBaseValues ruleBaseValues, RuleDelegationBo ruleDelegation, List errors) { if (errors == null) { errors = new ArrayList(); } if (getDocumentTypeService().findByName(ruleBaseValues.getDocTypeName()) == null) { errors.add(new WorkflowServiceErrorImpl("Document Type Invalid", "doctype.documenttypeservice.doctypename.required")); LOG.error("Document Type Invalid"); } if (ruleBaseValues.getToDateValue() == null) { try { ruleBaseValues.setToDateValue( new Timestamp(RiceConstants.getDefaultDateFormat().parse("01/01/2100").getTime())); } catch (ParseException e) { LOG.error("Error date-parsing default date"); throw new WorkflowServiceErrorException("Error parsing default date.", e); } } if (ruleBaseValues.getFromDateValue() == null) { ruleBaseValues.setFromDateValue(new Timestamp(System.currentTimeMillis())); } if (ruleBaseValues.getToDateValue().before(ruleBaseValues.getFromDateValue())) { errors.add(new WorkflowServiceErrorImpl("From Date is later than to date", "routetemplate.ruleservice.daterange.fromafterto")); LOG.error("From Date is later than to date"); } if (ruleBaseValues.getDescription() == null || ruleBaseValues.getDescription().equals("")) { errors.add(new WorkflowServiceErrorImpl("Description is required", "routetemplate.ruleservice.description.required")); LOG.error("Description is missing"); } for (Object element : ruleBaseValues.getRuleResponsibilities()) { RuleResponsibilityBo responsibility = (RuleResponsibilityBo) element; if (responsibility.getRuleResponsibilityName() != null && KewApiConstants.RULE_RESPONSIBILITY_GROUP_ID .equals(responsibility.getRuleResponsibilityType())) { if (getGroupService().getGroup(responsibility.getRuleResponsibilityName()) == null) { errors.add(new WorkflowServiceErrorImpl("Workgroup is invalid", "routetemplate.ruleservice.workgroup.invalid")); LOG.error("Workgroup is invalid"); } } else if (responsibility.getPrincipal() == null && responsibility.getRole() == null) { errors.add( new WorkflowServiceErrorImpl("User is invalid", "routetemplate.ruleservice.user.invalid")); LOG.error("User is invalid"); } else if (responsibility.isUsingRole()) { if (responsibility.getApprovePolicy() == null || !(responsibility.getApprovePolicy().equals(ActionRequestPolicy.ALL.getCode()) || responsibility.getApprovePolicy().equals(ActionRequestPolicy.FIRST.getCode()))) { errors.add(new WorkflowServiceErrorImpl("Approve Policy is Invalid", "routetemplate.ruleservice.approve.policy.invalid")); LOG.error("Approve Policy is Invalid"); } } } if (ruleBaseValues.getRuleTemplate() != null) { for (Object element : ruleBaseValues.getRuleTemplate().getActiveRuleTemplateAttributes()) { RuleTemplateAttributeBo templateAttribute = (RuleTemplateAttributeBo) element; if (!templateAttribute.isRuleValidationAttribute()) { continue; } RuleValidationAttribute attribute = templateAttribute.getRuleValidationAttribute(); UserSession userSession = GlobalVariables.getUserSession(); try { RuleValidationContext validationContext = RuleValidationContext.Builder .create(RuleBaseValues.to(ruleBaseValues), RuleDelegationBo.to(ruleDelegation), userSession.getPrincipalId()) .build(); ValidationResults results = attribute.validate(validationContext); if (results != null && !results.getErrors().isEmpty()) { errors.add(results); } } catch (Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException("Problem validation rule.", e); } } } if (ruleBaseValues.getRuleExpressionDef() != null) { // rule expressions do not require parse-/save-time validation } if (!errors.isEmpty()) { throw new WorkflowServiceErrorException("RuleBaseValues validation errors", errors); } } @Override public List<RuleBaseValues> findByDocumentId(String documentId) { return getRuleDAO().findByDocumentId(documentId); } @SuppressWarnings("rawtypes") @Override public List<RuleBaseValues> search(String docTypeName, String ruleId, String ruleTemplateId, String ruleDescription, String groupId, String principalId, Boolean delegateRule, Boolean activeInd, Map extensionValues, String workflowIdDirective) { return getRuleDAO().search(docTypeName, ruleId, ruleTemplateId, ruleDescription, groupId, principalId, delegateRule, activeInd, extensionValues, workflowIdDirective); } @SuppressWarnings("rawtypes") @Override public List<RuleBaseValues> searchByTemplate(String docTypeName, String ruleTemplateName, String ruleDescription, String groupId, String principalId, Boolean workgroupMember, Boolean delegateRule, Boolean activeInd, Map extensionValues, Collection<String> actionRequestCodes) { if ((StringUtils.isEmpty(docTypeName)) && (StringUtils.isEmpty(ruleTemplateName)) && (StringUtils.isEmpty(ruleDescription)) && (StringUtils.isEmpty(groupId)) && (StringUtils.isEmpty(principalId)) && (extensionValues.isEmpty()) && (actionRequestCodes.isEmpty())) { // all fields are empty throw new IllegalArgumentException("At least one criterion must be sent"); } RuleTemplateBo ruleTemplate = getRuleTemplateService().findByRuleTemplateName(ruleTemplateName); String ruleTemplateId = null; if (ruleTemplate != null) { ruleTemplateId = ruleTemplate.getId(); } if (((extensionValues != null) && (!extensionValues.isEmpty())) && (ruleTemplateId == null)) { // cannot have extensions without a correct template throw new IllegalArgumentException("A Rule Template Name must be given if using Rule Extension values"); } Collection<String> workgroupIds = new ArrayList<String>(); if (principalId != null) { KEWServiceLocator.getIdentityHelperService().validatePrincipalId(principalId); if ((workgroupMember == null) || (workgroupMember.booleanValue())) { workgroupIds = getGroupService().getGroupIdsByPrincipalId(principalId); } else { // user was passed but workgroups should not be parsed... do nothing } } else if (groupId != null) { Group group = KEWServiceLocator.getIdentityHelperService().getGroup(groupId); if (group == null) { throw new IllegalArgumentException("Group does not exist in for given group id: " + groupId); } else { workgroupIds.add(group.getId()); } } return getRuleDAO().search(docTypeName, ruleTemplateId, ruleDescription, workgroupIds, principalId, delegateRule, activeInd, extensionValues, actionRequestCodes); } @Override public void delete(String ruleBaseValuesId) { getRuleDAO().delete(ruleBaseValuesId); } @Override public RuleBaseValues findRuleBaseValuesById(String ruleBaseValuesId) { return getRuleDAO().findRuleBaseValuesById(ruleBaseValuesId); } @Override public RuleResponsibilityBo findRuleResponsibility(String responsibilityId) { return getRuleDAO().findRuleResponsibility(responsibilityId); } @Override public List fetchAllCurrentRulesForTemplateDocCombination(String ruleTemplateName, String documentType) { String ruleTemplateId = getRuleTemplateService().findByRuleTemplateName(ruleTemplateName).getId(); return getRuleDAO().fetchAllCurrentRulesForTemplateDocCombination(ruleTemplateId, getDocGroupAndTypeList(documentType)); } @Override public List fetchAllCurrentRulesForTemplateDocCombination(String ruleTemplateName, String documentType, Timestamp effectiveDate) { String ruleTemplateId = getRuleTemplateService().findByRuleTemplateName(ruleTemplateName).getId(); PerformanceLogger performanceLogger = new PerformanceLogger(); performanceLogger.log("Time to fetchRules by template " + ruleTemplateName + " not caching."); return getRuleDAO().fetchAllCurrentRulesForTemplateDocCombination(ruleTemplateId, getDocGroupAndTypeList(documentType), effectiveDate); } @SuppressWarnings("unchecked") @Override public List fetchAllRules(boolean currentRules) { return getRuleDAO().fetchAllRules(currentRules); } @SuppressWarnings("rawtypes") private List getDocGroupAndTypeList(String documentType) { List<String> docTypeList = new ArrayList<String>(); DocumentTypeService docTypeService = (DocumentTypeService) KEWServiceLocator .getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE); DocumentType docType = docTypeService.findByName(documentType); while (docType != null) { docTypeList.add(docType.getName()); docType = docType.getParentDocType(); } return docTypeList; } @SuppressWarnings("rawtypes") private Integer getNextVersionNumber(RuleBaseValues currentRule) { List<Integer> candidates = new ArrayList<Integer>(); candidates.add(currentRule.getVersionNbr()); List pendingRules = ruleDAO.findByPreviousRuleId(currentRule.getId()); for (Iterator iterator = pendingRules.iterator(); iterator.hasNext();) { RuleBaseValues pendingRule = (RuleBaseValues) iterator.next(); candidates.add(pendingRule.getVersionNbr()); } Collections.sort(candidates); Integer maxVersionNumber = (Integer) candidates.get(candidates.size() - 1); if (maxVersionNumber == null) { return Integer.valueOf(0); } return Integer.valueOf(maxVersionNumber.intValue() + 1); } /** * Determines if the given rule is locked for routing. * * In the case of a root rule edit, this method will take the rule id of the rule being edited. * * In the case of a new delegate rule or a delegate rule edit, this method will take the id of it's parent. */ @Override public String isLockedForRouting(String currentRuleBaseValuesId) { // checks for any other versions of the given rule, essentially, if this is a rule edit we want to see how many other // pending edits are out there List pendingRules = ruleDAO.findByPreviousRuleId(currentRuleBaseValuesId); boolean isDead = true; for (Iterator iterator = pendingRules.iterator(); iterator.hasNext();) { RuleBaseValues pendingRule = (RuleBaseValues) iterator.next(); if (pendingRule.getDocumentId() != null && StringUtils.isNotBlank(pendingRule.getDocumentId())) { DocumentRouteHeaderValue routeHeader = getRouteHeaderService() .getRouteHeader(pendingRule.getDocumentId()); // the pending edit is considered dead if it's been disapproved or cancelled and we are allowed to proceed with our own edit isDead = routeHeader.isDisaproved() || routeHeader.isCanceled(); if (!isDead) { return pendingRule.getDocumentId(); } } for (Object element : pendingRule.getRuleResponsibilities()) { RuleResponsibilityBo responsibility = (RuleResponsibilityBo) element; for (Object element2 : responsibility.getDelegationRules()) { RuleDelegationBo delegation = (RuleDelegationBo) element2; List pendingDelegateRules = ruleDAO .findByPreviousRuleId(delegation.getDelegationRule().getId()); for (Iterator iterator3 = pendingDelegateRules.iterator(); iterator3.hasNext();) { RuleBaseValues pendingDelegateRule = (RuleBaseValues) iterator3.next(); if (pendingDelegateRule.getDocumentId() != null && StringUtils.isNotBlank(pendingDelegateRule.getDocumentId())) { DocumentRouteHeaderValue routeHeader = getRouteHeaderService() .getRouteHeader(pendingDelegateRule.getDocumentId()); isDead = routeHeader.isDisaproved() || routeHeader.isCanceled(); if (!isDead) { return pendingDelegateRule.getDocumentId(); } } } } } } return null; } public RuleBaseValues getParentRule(RuleBaseValues rule) { if (rule == null || rule.getId() == null) { throw new IllegalArgumentException("Rule must be non-null with non-null id: " + rule); } if (!Boolean.TRUE.equals(rule.getDelegateRule())) { return null; } return getRuleDAO().getParentRule(rule.getId()); } /** * This configuration is currently stored in a system parameter named "CUSTOM_DOCUMENT_TYPES ", * long term we should come up with a better solution. The format of this constant is a comma-separated * list of entries of the following form: * * <<name of doc type on rule>>:<<rule template name on rule>>:<<type of rule>>:<<name of document type to use for rule routing>> * * Rule type indicates either main or delegation rules. A main rule is indicated by the character 'M' and a * delegate rule is indicated by the character 'D'. * * So, if you wanted to route "main" rules made for the "MyDocType" document with the rule template name * "MyRuleTemplate" using the "MyMainRuleDocType" doc type, it would be specified as follows: * * MyDocType:MyRuleTemplate:M:MyMainRuleDocType * * If you also wanted to route "delegate" rules made for the "MyDocType" document with rule template name * "MyDelegateTemplate" using the "MyDelegateRuleDocType", you would then set the constant as follows: * * MyDocType:MyRuleTemplate:M:MyMainRuleDocType,MyDocType:MyDelegateTemplate:D:MyDelegateRuleDocType * * TODO this method ended up being a mess, we should get rid of this as soon as we can */ @Override public String getRuleDocumentTypeName(List rules) { if (rules.size() == 0) { throw new IllegalArgumentException("Cannot determine rule DocumentType for an empty list of rules."); } String ruleDocTypeName = null; RuleRoutingConfig config = RuleRoutingConfig.parse(); // There are 2 cases here RuleBaseValues firstRule = (RuleBaseValues) rules.get(0); if (Boolean.TRUE.equals(firstRule.getDelegateRule())) { // if it's a delegate rule then the list will contain only 2 elements, the first is the delegate rule, // the second is the parent rule. In this case just look at the custom routing process for the delegate rule. ruleDocTypeName = config.getDocumentTypeName(firstRule); } else { // if this is a list of parent rules being routed, look at all configued routing types and verify that they are // all the same, if not throw an exception String parentRulesDocTypeName = null; for (Iterator iterator = rules.iterator(); iterator.hasNext();) { RuleBaseValues rule = (RuleBaseValues) iterator.next(); // if it's a delegate rule just skip it if (Boolean.TRUE.equals(rule.getDelegateRule())) { continue; } String currentDocTypeName = config.getDocumentTypeName(rule); if (parentRulesDocTypeName == null) { parentRulesDocTypeName = currentDocTypeName; } else { if (!ObjectUtils.equals(currentDocTypeName, parentRulesDocTypeName)) { throw new RuntimeException( "There are multiple rules being routed and they have different document type definitions! " + parentRulesDocTypeName + " and " + currentDocTypeName); } } } ruleDocTypeName = parentRulesDocTypeName; } if (ruleDocTypeName == null) { ruleDocTypeName = KewApiConstants.DEFAULT_RULE_DOCUMENT_NAME; } return ruleDocTypeName; } public void setRuleDAO(RuleDAO ruleDAO) { this.ruleDAO = ruleDAO; } public RuleDAO getRuleDAO() { return ruleDAO; } @Override public void deleteRuleResponsibilityById(String ruleResponsibilityId) { getDataObjectService().delete(ruleResponsibilityId); } @Override public RuleResponsibilityBo findByRuleResponsibilityId(String ruleResponsibilityId) { return getDataObjectService().find(RuleResponsibilityBo.class, ruleResponsibilityId); } @Override public List findRuleBaseValuesByResponsibilityReviewer(String reviewerName, String type) { return getRuleDAO().findRuleBaseValuesByResponsibilityReviewer(reviewerName, type); } @Override public List findRuleBaseValuesByResponsibilityReviewerTemplateDoc(String ruleTemplateName, String documentType, String reviewerName, String type) { return getRuleDAO().findRuleBaseValuesByResponsibilityReviewerTemplateDoc(ruleTemplateName, documentType, reviewerName, type); } public RuleTemplateService getRuleTemplateService() { return (RuleTemplateService) KEWServiceLocator.getService(KEWServiceLocator.RULE_TEMPLATE_SERVICE); } public DocumentTypeService getDocumentTypeService() { return (DocumentTypeService) KEWServiceLocator.getService(KEWServiceLocator.DOCUMENT_TYPE_SERVICE); } public GroupService getGroupService() { return KimApiServiceLocator.getGroupService(); } public ActionRequestService getActionRequestService() { return (ActionRequestService) KEWServiceLocator.getService(KEWServiceLocator.ACTION_REQUEST_SRV); } private ResponsibilityIdService getResponsibilityIdService() { return (ResponsibilityIdService) KEWServiceLocator.getService(KEWServiceLocator.RESPONSIBILITY_ID_SERVICE); } private RuleDelegationService getRuleDelegationService() { return (RuleDelegationService) KEWServiceLocator.getService(KEWServiceLocator.RULE_DELEGATION_SERVICE); } private RouteHeaderService getRouteHeaderService() { return (RouteHeaderService) KEWServiceLocator.getService(KEWServiceLocator.DOC_ROUTE_HEADER_SRV); } /** * A comparator implementation which compares RuleBaseValues and puts all delegate rules first. */ public class RuleDelegationSorter implements Comparator { @Override public int compare(Object arg0, Object arg1) { RuleBaseValues rule1 = (RuleBaseValues) arg0; RuleBaseValues rule2 = (RuleBaseValues) arg1; Integer rule1Value = new Integer((rule1.getDelegateRule().booleanValue() ? 0 : 1)); Integer rule2Value = new Integer((rule2.getDelegateRule().booleanValue() ? 0 : 1)); int value = rule1Value.compareTo(rule2Value); return value; } } @Override public void loadXml(InputStream inputStream, String principalId) { RuleXmlParser parser = new RuleXmlParser(); try { parser.parseRules(inputStream); } catch (Exception e) { //any other exception LOG.error("Error loading xml file", e); WorkflowServiceErrorException wsee = new WorkflowServiceErrorException("Error loading xml file", new WorkflowServiceErrorImpl("Error loading xml file", XML_PARSE_ERROR)); wsee.initCause(e); throw wsee; } } @Override public Element export(ExportDataSet dataSet) { RuleXmlExporter exporter = new RuleXmlExporter(XmlConstants.RULE_NAMESPACE); return exporter.export(dataSet); } @Override public boolean supportPrettyPrint() { return true; } protected List<RuleBaseValues> loadRules(List<String> ruleIds) { List<RuleBaseValues> rules = new ArrayList<RuleBaseValues>(); for (String ruleId : ruleIds) { RuleBaseValues rule = KEWServiceLocator.getRuleService().findRuleBaseValuesById(ruleId); rules.add(rule); } return rules; } /** * If a rule has been modified and is no longer current since the original request was made, we need to * be sure to NOT update the rule. */ protected boolean shouldChangeRuleInvolvement(String documentId, RuleBaseValues rule) { if (!rule.getCurrentInd()) { LOG.warn("Rule requested for rule involvement change by document " + documentId + " is no longer current. Change will not be executed! Rule id is: " + rule.getId()); return false; } String lockingDocumentId = KEWServiceLocator.getRuleService().isLockedForRouting(rule.getId()); if (lockingDocumentId != null) { LOG.warn("Rule requested for rule involvement change by document " + documentId + " is locked by document " + lockingDocumentId + " and cannot be modified. " + "Change will not be executed! Rule id is: " + rule.getId()); return false; } return true; } protected RuleDelegationBo getRuleDelegationForDelegateRule(RuleBaseValues rule) { if (Boolean.TRUE.equals(rule.getDelegateRule())) { List delegations = getRuleDelegationService().findByDelegateRuleId(rule.getId()); for (Iterator iterator = delegations.iterator(); iterator.hasNext();) { RuleDelegationBo ruleDelegation = (RuleDelegationBo) iterator.next(); RuleBaseValues parentRule = ruleDelegation.getRuleResponsibility().getRuleBaseValues(); if (Boolean.TRUE.equals(parentRule.getCurrentInd())) { return ruleDelegation; } } } return null; } protected void hookUpDelegateRuleToParentRule(RuleBaseValues newParentRule, RuleBaseValues newDelegationRule, RuleDelegationBo existingRuleDelegation) { // hook up parent rule to new rule delegation boolean foundDelegation = false; outer: for (RuleResponsibilityBo responsibility : newParentRule.getRuleResponsibilities()) { for (RuleDelegationBo ruleDelegation : responsibility.getDelegationRules()) { if (ruleDelegation.getDelegationRule().getId() .equals(existingRuleDelegation.getDelegationRule().getId())) { ruleDelegation.setDelegationRule(newDelegationRule); foundDelegation = true; break outer; } } } if (!foundDelegation) { throw new WorkflowRuntimeException("Failed to locate the existing rule delegation with id: " + existingRuleDelegation.getDelegationRule().getId()); } } protected RuleBaseValues createNewRuleVersion(RuleBaseValues existingRule, String documentId) throws Exception { RuleBaseValues rule = new RuleBaseValues(); PropertyUtils.copyProperties(rule, existingRule); rule.setPreviousVersion(existingRule); rule.setPreviousRuleId(existingRule.getId()); rule.setId(null); rule.setActivationDate(null); rule.setDeactivationDate(null); rule.setVersionNumber(null); rule.setDocumentId(documentId); // TODO: FIXME: need to copy the rule expression here too? rule.setRuleResponsibilities(new ArrayList<RuleResponsibilityBo>()); for (RuleResponsibilityBo existingResponsibility : existingRule.getRuleResponsibilities()) { RuleResponsibilityBo responsibility = new RuleResponsibilityBo(); PropertyUtils.copyProperties(responsibility, existingResponsibility); responsibility.setRuleBaseValues(rule); responsibility.setRuleBaseValuesId(null); responsibility.setId(null); responsibility.setVersionNumber(null); rule.getRuleResponsibilities().add(responsibility); // responsibility.setDelegationRules(new ArrayList()); // for (RuleDelegation existingDelegation : (List<RuleDelegation>)existingResponsibility.getDelegationRules()) { // RuleDelegation delegation = new RuleDelegation(); // PropertyUtils.copyProperties(delegation, existingDelegation); // delegation.setRuleDelegationId(null); // delegation.setRuleResponsibility(responsibility); // delegation.setRuleResponsibilityId(null); // delegation.setVersionNumber(0L); // // it's very important that we do NOT recurse down into the delegation rules and reversion those, // // this is important to how rule versioning works // responsibility.getDelegationRules().add(delegation); // } } rule.setRuleExtensions(new ArrayList()); for (RuleExtensionBo existingExtension : existingRule.getRuleExtensions()) { RuleExtensionBo extension = new RuleExtensionBo(); PropertyUtils.copyProperties(extension, existingExtension); extension.setVersionNumber(new Long(0)); extension.setRuleBaseValues(rule); extension.setRuleBaseValuesId(null); extension.setRuleExtensionId(null); rule.getRuleExtensions().add(extension); extension.setExtensionValues(new ArrayList<RuleExtensionValue>()); for (RuleExtensionValue existingExtensionValue : extension.getExtensionValues()) { RuleExtensionValue extensionValue = new RuleExtensionValue(); PropertyUtils.copyProperties(extensionValue, existingExtensionValue); extensionValue.setExtension(extension); extensionValue.getExtension().setRuleExtensionId(null); extensionValue.setLockVerNbr(0); extensionValue.setRuleExtensionValueId(null); extension.getExtensionValues().add(extensionValue); } } return rule; } private static class RuleVersion { public RuleBaseValues rule; public RuleBaseValues parent; public RuleDelegationBo delegation; } private static class RuleRoutingConfig { private List configs = new ArrayList(); public static RuleRoutingConfig parse() { RuleRoutingConfig config = new RuleRoutingConfig(); String constant = CoreFrameworkServiceLocator.getParameterService().getParameterValueAsString( KewApiConstants.KEW_NAMESPACE, KRADConstants.DetailTypes.RULE_DETAIL_TYPE, KewApiConstants.RULE_CUSTOM_DOC_TYPES); if (!StringUtils.isEmpty(constant)) { String[] ruleConfigs = constant.split(","); for (String ruleConfig : ruleConfigs) { String[] configElements = ruleConfig.split(":"); if (configElements.length != 4) { throw new RuntimeException( "Found incorrect number of config elements within a section of the custom rule document types config. There should have been four ':' delimited sections! " + ruleConfig); } config.configs.add(configElements); } } return config; } public String getDocumentTypeName(RuleBaseValues rule) { for (Iterator iterator = configs.iterator(); iterator.hasNext();) { String[] configElements = (String[]) iterator.next(); String docTypeName = configElements[0]; String ruleTemplateName = configElements[1]; String type = configElements[2]; String ruleDocTypeName = configElements[3]; if (rule.getDocTypeName().equals(docTypeName) && rule.getRuleTemplateName().equals(ruleTemplateName)) { if (type.equals("M")) { if (Boolean.FALSE.equals(rule.getDelegateRule())) { return ruleDocTypeName; } } else if (type.equals("D")) { if (Boolean.TRUE.equals(rule.getDelegateRule())) { return ruleDocTypeName; } } else { throw new RuntimeException("Bad rule type '" + type + "' in rule doc type routing config."); } } } return null; } } @Override public String getDuplicateRuleId(RuleBaseValues rule) { // TODO: this method is extremely slow, if we could implement a more optimized query here, that would help tremendously Rule baseRule = RuleBaseValues.to(rule); List<RuleResponsibility> responsibilities = baseRule.getRuleResponsibilities(); List<RuleExtension> extensions = baseRule.getRuleExtensions(); String docTypeName = baseRule.getDocTypeName(); String ruleTemplateName = baseRule.getRuleTemplateName(); //use api service to take advantage of caching List<Rule> rules = KewApiServiceLocator.getRuleService() .getRulesByTemplateNameAndDocumentTypeName(ruleTemplateName, docTypeName); for (Rule r : rules) { if (ObjectUtils.equals(rule.isActive(), r.isActive()) && ObjectUtils.equals(docTypeName, r.getDocTypeName()) && ObjectUtils.equals(ruleTemplateName, r.getRuleTemplateName()) && CollectionUtils.collectionsEquivalent(responsibilities, r.getRuleResponsibilities()) && CollectionUtils.collectionsEquivalent(extensions, r.getRuleExtensions())) { if (ObjectUtils.equals(baseRule.getRuleExpressionDef(), r.getRuleExpressionDef()) || (baseRule.getRuleExpressionDef() != null && r.getRuleExpressionDef() != null) && ObjectUtils.equals(baseRule.getRuleExpressionDef().getType(), r.getRuleExpressionDef().getType()) && ObjectUtils.equals(baseRule.getRuleExpressionDef().getExpression(), r.getRuleExpressionDef().getExpression())) { // we have a duplicate return r.getId(); } } } return null; } private void generateRuleNameIfNeeded(RuleBaseValues rule) { if (StringUtils.isBlank(rule.getName())) { rule.setName(UUID.randomUUID().toString()); } } private void assignResponsibilityIds(RuleBaseValues rule) { for (RuleResponsibilityBo responsibility : rule.getRuleResponsibilities()) { if (responsibility.getResponsibilityId() == null) { responsibility.setResponsibilityId( KEWServiceLocator.getResponsibilityIdService().getNewResponsibilityId()); } } } @Override public RuleBaseValues saveRule(RuleBaseValues rule, boolean isRetroactiveUpdatePermitted) { rule.setPreviousRuleId(rule.getId()); rule.setPreviousVersion(null); rule.setId(null); makeCurrent(rule, isRetroactiveUpdatePermitted); return getRuleDAO().findRuleBaseValuesByName(rule.getName()); } @Override public List<RuleBaseValues> saveRules(List<RuleBaseValues> rulesToSave, boolean isRetroactiveUpdatePermitted) { List<RuleBaseValues> savedRules = new ArrayList<RuleBaseValues>(); for (RuleBaseValues rule : rulesToSave) { rule = saveRule(rule, isRetroactiveUpdatePermitted); savedRules.add(rule); } return savedRules; } @Override public RuleDelegationBo saveRuleDelegation(RuleDelegationBo ruleDelegation, boolean isRetroactiveUpdatePermitted) { RuleBaseValues rule = ruleDelegation.getDelegationRule(); //rule = (RuleBaseValues)SerializationUtils.deepCopy(rule); rule.setPreviousRuleId(rule.getId()); rule.setPreviousVersion(null); rule.setId(null); ruleDelegation.setRuleDelegationId(null); ruleDelegation.setDelegationRule(rule); makeCurrent(ruleDelegation, isRetroactiveUpdatePermitted); return ruleDelegation; } @Override public List<RuleDelegationBo> saveRuleDelegations(List<RuleDelegationBo> ruleDelegationsToSave, boolean isRetroactiveUpdatePermitted) { List<RuleDelegationBo> savedRuleDelegations = new ArrayList<RuleDelegationBo>(); for (RuleDelegationBo ruleDelegation : ruleDelegationsToSave) { ruleDelegation = saveRuleDelegation(ruleDelegation, isRetroactiveUpdatePermitted); savedRuleDelegations.add(ruleDelegation); } return savedRuleDelegations; } @Override public String findResponsibilityIdForRule(String ruleName, String ruleResponsibilityName, String ruleResponsibilityType) { return getRuleDAO().findResponsibilityIdForRule(ruleName, ruleResponsibilityName, ruleResponsibilityType); } protected String getRuleByTemplateAndDocTypeCacheKey(String ruleTemplateName, String docTypeName) { return "'templateName=' + " + ruleTemplateName + " '|' + 'documentTypeName=' + " + docTypeName; } public DataObjectService getDataObjectService() { return dataObjectService; } @Required public void setDataObjectService(DataObjectService dataObjectService) { this.dataObjectService = dataObjectService; } }