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 }