001 /**
002 * Copyright 2005-2012 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.DictionaryValidationService;
037 import org.kuali.rice.krad.service.DocumentDictionaryService;
038 import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
039 import org.kuali.rice.krad.service.KualiRuleService;
040 import org.kuali.rice.krad.util.GlobalVariables;
041 import org.kuali.rice.krad.util.KRADConstants;
042 import org.kuali.rice.krad.util.MessageMap;
043
044 /**
045 * This class represents a rule evaluator for Kuali. This class is to be used for evaluating business rule checks. The class defines
046 * one method right now - applyRules() which takes in a Document and a DocumentEvent and does the proper business rule checks based
047 * on the context of the event and the document type.
048 */
049 public class KualiRuleServiceImpl implements KualiRuleService {
050 private static final Logger LOG = Logger.getLogger(KualiRuleServiceImpl.class);
051
052 private DocumentDictionaryService documentDictionaryService;
053 private DictionaryValidationService dictionaryValidationService;
054 private DataDictionaryService dataDictionaryService;
055
056 /**
057 * @see org.kuali.rice.krad.service.KualiRuleService#applyRules(org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent)
058 */
059 public boolean applyRules(KualiDocumentEvent event) {
060 if (event == null) {
061 throw new IllegalArgumentException("invalid (null) event");
062 }
063
064 event.validate();
065 if ( LOG.isDebugEnabled() ) {
066 LOG.debug("calling applyRules for event " + event);
067 }
068
069 BusinessRule rule = getBusinessRulesInstance(event.getDocument(), event.getRuleInterfaceClass());
070
071 boolean success = true;
072 if (rule != null) {
073 if ( LOG.isDebugEnabled() ) {
074 LOG.debug("processing " + event.getName() + " with rule " + rule.getClass().getName());
075 }
076 increaseErrorPath(event.getErrorPathPrefix());
077
078 // get any child events and apply rules
079 List<KualiDocumentEvent> events = event.generateEvents();
080 for (KualiDocumentEvent generatedEvent : events) {
081 success &= applyRules(generatedEvent);
082 }
083
084 // now call the event rule method
085 success &= event.invokeRuleMethod(rule);
086
087 decreaseErrorPath(event.getErrorPathPrefix());
088
089 // report failures
090 if (!success) {
091 if ( LOG.isDebugEnabled() ) { // NO, this is not a type - only log if in debug mode - this is not an error in production
092 LOG.debug(event.getName() + " businessRule " + rule.getClass().getName() + " failed");
093 }
094 }
095 else {
096 if ( LOG.isDebugEnabled() ) {
097 LOG.debug("processed " + event.getName() + " for rule " + rule.getClass().getName());
098 }
099 }
100
101 }
102 return success;
103 }
104
105 /**
106 * Builds a list containing AddAdHocRoutePersonEvents since the validation done for an AdHocRouteRecipient is the same for all
107 * events.
108 *
109 * @see org.kuali.rice.krad.service.KualiRuleService#generateAdHocRoutePersonEvents(org.kuali.rice.krad.document.Document)
110 */
111 public List<AddAdHocRoutePersonEvent> generateAdHocRoutePersonEvents(Document document) {
112 List<AdHocRoutePerson> adHocRoutePersons = document.getAdHocRoutePersons();
113
114 List<AddAdHocRoutePersonEvent> events = new ArrayList<AddAdHocRoutePersonEvent>();
115
116 for (int i = 0; i < adHocRoutePersons.size(); i++) {
117 events.add(new AddAdHocRoutePersonEvent(
118 KRADConstants.EXISTING_AD_HOC_ROUTE_PERSON_PROPERTY_NAME + "[" + i + "]", document, adHocRoutePersons.get(i)));
119 }
120
121 return events;
122 }
123
124 /**
125 * Builds a list containing AddAdHocRoutePersonEvents since the validation done for an AdHocRouteRecipient is the same for all
126 * events.
127 *
128 * @see org.kuali.rice.krad.service.KualiRuleService#generateAdHocRouteWorkgroupEvents(org.kuali.rice.krad.document.Document)
129 */
130 public List<AddAdHocRouteWorkgroupEvent> generateAdHocRouteWorkgroupEvents(Document document) {
131 List<AdHocRouteWorkgroup> adHocRouteWorkgroups = document.getAdHocRouteWorkgroups();
132
133 List<AddAdHocRouteWorkgroupEvent> events = new ArrayList<AddAdHocRouteWorkgroupEvent>();
134
135 for (int i = 0; i < adHocRouteWorkgroups.size(); i++) {
136 events.add(new AddAdHocRouteWorkgroupEvent(
137 KRADConstants.EXISTING_AD_HOC_ROUTE_WORKGROUP_PROPERTY_NAME + "[" + i + "]", document, adHocRouteWorkgroups.get(i)));
138 }
139
140 return events;
141 }
142
143
144
145
146
147
148 /**
149 * @param document
150 * @param ruleInterface
151 * @return instance of the businessRulesClass for the given document's type, if that businessRulesClass implements the given
152 * ruleInterface
153 */
154 public BusinessRule getBusinessRulesInstance(Document document, Class<? extends BusinessRule> ruleInterface) {
155 // get the businessRulesClass
156 Class<? extends BusinessRule> businessRulesClass = null;
157 if (document instanceof TransactionalDocument) {
158 TransactionalDocument transactionalDocument = (TransactionalDocument) document;
159
160 businessRulesClass = getDocumentDictionaryService().getBusinessRulesClass(transactionalDocument);
161 if (businessRulesClass == null) {
162 return new TransactionalDocumentRuleBase(); // default to a generic rule that will enforce Required fields
163 }
164 }
165 else if (document instanceof MaintenanceDocument) {
166 MaintenanceDocument maintenanceDocument = (MaintenanceDocument) document;
167
168 businessRulesClass = getDocumentDictionaryService().getBusinessRulesClass(maintenanceDocument);
169 if (businessRulesClass == null) {
170 return new MaintenanceDocumentRuleBase(); // default to a generic rule that will enforce Required fields
171 }
172 }
173 else {
174 LOG.error("unable to get businessRulesClass for unknown document type '" + document.getClass().getName() + "'");
175 }
176
177 // instantiate and return it if it implements the given ruleInterface
178 BusinessRule rule = null;
179 if (businessRulesClass != null) {
180 try {
181 if (ruleInterface.isAssignableFrom(businessRulesClass)) {
182 rule = businessRulesClass.newInstance();
183 }
184 }
185 catch (IllegalAccessException e) {
186 throw new InfrastructureException("error processing business rules", e);
187 }
188 catch (InstantiationException e) {
189 throw new InfrastructureException("error processing business rules", e);
190 }
191 }
192
193 return rule;
194 }
195
196 /**
197 * This method increases the registered error path, so that field highlighting can occur on the appropriate object attribute.
198 *
199 * @param errorPathPrefix
200 */
201 private void increaseErrorPath(String errorPathPrefix) {
202 MessageMap errorMap = GlobalVariables.getMessageMap();
203
204 if (!StringUtils.isBlank(errorPathPrefix)) {
205 errorMap.addToErrorPath(errorPathPrefix);
206 }
207 }
208
209 /**
210 * This method decreases the registered error path, so that field highlighting can occur on the appropriate object attribute.
211 *
212 * @param errorPathPrefix
213 */
214 private void decreaseErrorPath(String errorPathPrefix) {
215 MessageMap errorMap = GlobalVariables.getMessageMap();
216
217 if (!StringUtils.isBlank(errorPathPrefix)) {
218 errorMap.removeFromErrorPath(errorPathPrefix);
219 }
220 }
221
222 public DocumentDictionaryService getDocumentDictionaryService() {
223 if (documentDictionaryService == null) {
224 this.documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService();
225 }
226 return documentDictionaryService;
227 }
228
229 public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) {
230 this.documentDictionaryService = documentDictionaryService;
231 }
232
233 public DictionaryValidationService getDictionaryValidationService() {
234 return dictionaryValidationService;
235 }
236
237 public void setDictionaryValidationService(DictionaryValidationService dictionaryValidationService) {
238 this.dictionaryValidationService = dictionaryValidationService;
239 }
240
241 public DataDictionaryService getDataDictionaryService() {
242 return dataDictionaryService;
243 }
244
245 public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
246 this.dataDictionaryService = dataDictionaryService;
247 }
248 }