001 /** 002 * Copyright 2005-2013 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.rice.krad.service.impl; 017 018 import java.util.ArrayList; 019 import java.util.List; 020 021 import org.apache.commons.lang.StringUtils; 022 import org.apache.log4j.Logger; 023 import org.kuali.rice.krad.bo.AdHocRoutePerson; 024 import org.kuali.rice.krad.bo.AdHocRouteWorkgroup; 025 import org.kuali.rice.krad.document.Document; 026 import org.kuali.rice.krad.maintenance.MaintenanceDocument; 027 import org.kuali.rice.krad.document.TransactionalDocument; 028 import org.kuali.rice.krad.exception.InfrastructureException; 029 import org.kuali.rice.krad.rules.MaintenanceDocumentRuleBase; 030 import org.kuali.rice.krad.rules.TransactionalDocumentRuleBase; 031 import org.kuali.rice.krad.rules.rule.BusinessRule; 032 import org.kuali.rice.krad.rules.rule.event.AddAdHocRoutePersonEvent; 033 import org.kuali.rice.krad.rules.rule.event.AddAdHocRouteWorkgroupEvent; 034 import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent; 035 import org.kuali.rice.krad.service.DataDictionaryService; 036 import org.kuali.rice.krad.service.DocumentDictionaryService; 037 import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 038 import org.kuali.rice.krad.service.KualiRuleService; 039 import org.kuali.rice.krad.util.GlobalVariables; 040 import org.kuali.rice.krad.util.KRADConstants; 041 import org.kuali.rice.krad.util.MessageMap; 042 043 /** 044 * This class represents a rule evaluator for Kuali. This class is to be used for evaluating business rule checks. The class defines 045 * one method right now - applyRules() which takes in a Document and a DocumentEvent and does the proper business rule checks based 046 * on the context of the event and the document type. 047 */ 048 public class KualiRuleServiceImpl implements KualiRuleService { 049 private static final Logger LOG = Logger.getLogger(KualiRuleServiceImpl.class); 050 051 private DocumentDictionaryService documentDictionaryService; 052 private DataDictionaryService dataDictionaryService; 053 054 /** 055 * @see org.kuali.rice.krad.service.KualiRuleService#applyRules(org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent) 056 */ 057 public boolean applyRules(KualiDocumentEvent event) { 058 if (event == null) { 059 throw new IllegalArgumentException("invalid (null) event"); 060 } 061 062 event.validate(); 063 if ( LOG.isDebugEnabled() ) { 064 LOG.debug("calling applyRules for event " + event); 065 } 066 067 BusinessRule rule = getBusinessRulesInstance(event.getDocument(), event.getRuleInterfaceClass()); 068 069 boolean success = true; 070 if (rule != null) { 071 if ( LOG.isDebugEnabled() ) { 072 LOG.debug("processing " + event.getName() + " with rule " + rule.getClass().getName()); 073 } 074 increaseErrorPath(event.getErrorPathPrefix()); 075 076 // get any child events and apply rules 077 List<KualiDocumentEvent> events = event.generateEvents(); 078 for (KualiDocumentEvent generatedEvent : events) { 079 success &= applyRules(generatedEvent); 080 } 081 082 // now call the event rule method 083 success &= event.invokeRuleMethod(rule); 084 085 decreaseErrorPath(event.getErrorPathPrefix()); 086 087 // KULRICE-7930 Check for field validation errors 088 if(GlobalVariables.getMessageMap().hasErrors()) { 089 success = false; 090 } 091 092 // report failures 093 if (!success) { 094 if ( LOG.isDebugEnabled() ) { // NO, this is not a type - only log if in debug mode - this is not an error in production 095 LOG.debug(event.getName() + " businessRule " + rule.getClass().getName() + " failed"); 096 } 097 } 098 else { 099 if ( LOG.isDebugEnabled() ) { 100 LOG.debug("processed " + event.getName() + " for rule " + rule.getClass().getName()); 101 } 102 } 103 104 } 105 return success; 106 } 107 108 /** 109 * Builds a list containing AddAdHocRoutePersonEvents since the validation done for an AdHocRouteRecipient is the same for all 110 * events. 111 * 112 * @see org.kuali.rice.krad.service.KualiRuleService#generateAdHocRoutePersonEvents(org.kuali.rice.krad.document.Document) 113 */ 114 public List<AddAdHocRoutePersonEvent> generateAdHocRoutePersonEvents(Document document) { 115 List<AdHocRoutePerson> adHocRoutePersons = document.getAdHocRoutePersons(); 116 117 List<AddAdHocRoutePersonEvent> events = new ArrayList<AddAdHocRoutePersonEvent>(); 118 119 for (int i = 0; i < adHocRoutePersons.size(); i++) { 120 events.add(new AddAdHocRoutePersonEvent( 121 KRADConstants.EXISTING_AD_HOC_ROUTE_PERSON_PROPERTY_NAME + "[" + i + "]", document, adHocRoutePersons.get(i))); 122 } 123 124 return events; 125 } 126 127 /** 128 * Builds a list containing AddAdHocRoutePersonEvents since the validation done for an AdHocRouteRecipient is the same for all 129 * events. 130 * 131 * @see org.kuali.rice.krad.service.KualiRuleService#generateAdHocRouteWorkgroupEvents(org.kuali.rice.krad.document.Document) 132 */ 133 public List<AddAdHocRouteWorkgroupEvent> generateAdHocRouteWorkgroupEvents(Document document) { 134 List<AdHocRouteWorkgroup> adHocRouteWorkgroups = document.getAdHocRouteWorkgroups(); 135 136 List<AddAdHocRouteWorkgroupEvent> events = new ArrayList<AddAdHocRouteWorkgroupEvent>(); 137 138 for (int i = 0; i < adHocRouteWorkgroups.size(); i++) { 139 events.add(new AddAdHocRouteWorkgroupEvent( 140 KRADConstants.EXISTING_AD_HOC_ROUTE_WORKGROUP_PROPERTY_NAME + "[" + i + "]", document, adHocRouteWorkgroups.get(i))); 141 } 142 143 return events; 144 } 145 146 147 148 149 150 151 /** 152 * @param document 153 * @param ruleInterface 154 * @return instance of the businessRulesClass for the given document's type, if that businessRulesClass implements the given 155 * ruleInterface 156 */ 157 public BusinessRule getBusinessRulesInstance(Document document, Class<? extends BusinessRule> ruleInterface) { 158 // get the businessRulesClass 159 Class<? extends BusinessRule> businessRulesClass = null; 160 if (document instanceof TransactionalDocument) { 161 TransactionalDocument transactionalDocument = (TransactionalDocument) document; 162 163 businessRulesClass = getDocumentDictionaryService().getBusinessRulesClass(transactionalDocument); 164 if (businessRulesClass == null) { 165 return new TransactionalDocumentRuleBase(); // default to a generic rule that will enforce Required fields 166 } 167 } 168 else if (document instanceof MaintenanceDocument) { 169 MaintenanceDocument maintenanceDocument = (MaintenanceDocument) document; 170 171 businessRulesClass = getDocumentDictionaryService().getBusinessRulesClass(maintenanceDocument); 172 if (businessRulesClass == null) { 173 return new MaintenanceDocumentRuleBase(); // default to a generic rule that will enforce Required fields 174 } 175 } 176 else { 177 LOG.error("unable to get businessRulesClass for unknown document type '" + document.getClass().getName() + "'"); 178 } 179 180 // instantiate and return it if it implements the given ruleInterface 181 BusinessRule rule = null; 182 if (businessRulesClass != null) { 183 try { 184 if (ruleInterface.isAssignableFrom(businessRulesClass)) { 185 rule = businessRulesClass.newInstance(); 186 } 187 } 188 catch (IllegalAccessException e) { 189 throw new InfrastructureException("error processing business rules", e); 190 } 191 catch (InstantiationException e) { 192 throw new InfrastructureException("error processing business rules", e); 193 } 194 } 195 196 return rule; 197 } 198 199 /** 200 * This method increases the registered error path, so that field highlighting can occur on the appropriate object attribute. 201 * 202 * @param errorPathPrefix 203 */ 204 private void increaseErrorPath(String errorPathPrefix) { 205 MessageMap errorMap = GlobalVariables.getMessageMap(); 206 207 if (!StringUtils.isBlank(errorPathPrefix)) { 208 errorMap.addToErrorPath(errorPathPrefix); 209 } 210 } 211 212 /** 213 * This method decreases the registered error path, so that field highlighting can occur on the appropriate object attribute. 214 * 215 * @param errorPathPrefix 216 */ 217 private void decreaseErrorPath(String errorPathPrefix) { 218 MessageMap errorMap = GlobalVariables.getMessageMap(); 219 220 if (!StringUtils.isBlank(errorPathPrefix)) { 221 errorMap.removeFromErrorPath(errorPathPrefix); 222 } 223 } 224 225 public DocumentDictionaryService getDocumentDictionaryService() { 226 if (documentDictionaryService == null) { 227 this.documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService(); 228 } 229 return documentDictionaryService; 230 } 231 232 public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) { 233 this.documentDictionaryService = documentDictionaryService; 234 } 235 236 public DataDictionaryService getDataDictionaryService() { 237 return dataDictionaryService; 238 } 239 240 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) { 241 this.dataDictionaryService = dataDictionaryService; 242 } 243 }