View Javadoc
1   /**
2    * Copyright 2005-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.service.impl;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import org.apache.commons.lang.StringUtils;
22  import org.apache.log4j.Logger;
23  import org.kuali.rice.core.api.exception.RiceRuntimeException;
24  import org.kuali.rice.krad.bo.AdHocRoutePerson;
25  import org.kuali.rice.krad.bo.AdHocRouteWorkgroup;
26  import org.kuali.rice.krad.document.Document;
27  import org.kuali.rice.krad.maintenance.MaintenanceDocument;
28  import org.kuali.rice.krad.document.TransactionalDocument;
29  import org.kuali.rice.krad.rules.MaintenanceDocumentRuleBase;
30  import org.kuali.rice.krad.rules.TransactionalDocumentRuleBase;
31  import org.kuali.rice.krad.rules.rule.BusinessRule;
32  import org.kuali.rice.krad.rules.rule.event.AddAdHocRoutePersonEvent;
33  import org.kuali.rice.krad.rules.rule.event.AddAdHocRouteWorkgroupEvent;
34  import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent;
35  import org.kuali.rice.krad.service.DataDictionaryService;
36  import org.kuali.rice.krad.service.DocumentDictionaryService;
37  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
38  import org.kuali.rice.krad.service.KualiRuleService;
39  import org.kuali.rice.krad.util.GlobalVariables;
40  import org.kuali.rice.krad.util.KRADConstants;
41  import org.kuali.rice.krad.util.MessageMap;
42  
43  /**
44   * Represents a rule evaluator for Kuali. This class is to be used for evaluating business rule checks. The class
45   * defines one method right now - applyRules() which takes in a Document and a DocumentEvent and does the proper
46   * business rule checks based on the context of the event and the document type
47   *
48   * @author Kuali Rice Team (rice.collab@kuali.org)
49   */
50  public class KualiRuleServiceImpl implements KualiRuleService {
51      private static final Logger LOG = Logger.getLogger(KualiRuleServiceImpl.class);
52  
53      private DocumentDictionaryService documentDictionaryService;
54      private DataDictionaryService dataDictionaryService;
55  
56      /**
57       * @see org.kuali.rice.krad.service.KualiRuleService#applyRules(org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent)
58       */
59      public boolean applyRules(KualiDocumentEvent event) {
60          if (event == null) {
61              throw new IllegalArgumentException("invalid (null) event");
62          }
63  
64          event.validate();
65          if (LOG.isDebugEnabled()) {
66              LOG.debug("calling applyRules for event " + event);
67          }
68  
69          BusinessRule rule = getBusinessRulesInstance(event.getDocument(), event.getRuleInterfaceClass());
70  
71          boolean success = true;
72          if (rule != null) {
73              if (LOG.isDebugEnabled()) {
74                  LOG.debug("processing " + event.getName() + " with rule " + rule.getClass().getName());
75              }
76              increaseErrorPath(event.getErrorPathPrefix());
77  
78              // get any child events and apply rules
79              List<KualiDocumentEvent> events = event.generateEvents();
80              for (KualiDocumentEvent generatedEvent : events) {
81                  success &= applyRules(generatedEvent);
82              }
83  
84              // now call the event rule method
85              success &= event.invokeRuleMethod(rule);
86  
87              decreaseErrorPath(event.getErrorPathPrefix());
88  
89              // report failures
90              if (!success) {
91                  if (LOG.isDebugEnabled()) { // NO, this is not a type - only log if in debug mode - this is not an error in production
92                      LOG.debug(event.getName() + " businessRule " + rule.getClass().getName() + " failed");
93                  }
94              } else {
95                  if (LOG.isDebugEnabled()) {
96                      LOG.debug("processed " + event.getName() + " for rule " + rule.getClass().getName());
97                  }
98              }
99  
100         }
101         return success;
102     }
103 
104     /**
105      * Builds a list containing AddAdHocRoutePersonEvents since the validation done for an AdHocRouteRecipient is the
106      * same for all events
107      *
108      * @see org.kuali.rice.krad.service.KualiRuleService#generateAdHocRoutePersonEvents(org.kuali.rice.krad.document.Document)
109      */
110     public List<AddAdHocRoutePersonEvent> generateAdHocRoutePersonEvents(Document document) {
111         List<AdHocRoutePerson> adHocRoutePersons = document.getAdHocRoutePersons();
112 
113         List<AddAdHocRoutePersonEvent> events = new ArrayList<AddAdHocRoutePersonEvent>();
114 
115         for (int i = 0; i < adHocRoutePersons.size(); i++) {
116             events.add(new AddAdHocRoutePersonEvent(
117                     KRADConstants.EXISTING_AD_HOC_ROUTE_PERSON_PROPERTY_NAME + "[" + i + "]", document,
118                     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
126      * same for all 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,
138                     adHocRouteWorkgroups.get(i)));
139         }
140 
141         return events;
142     }
143 
144     /**
145      * @param document
146      * @param ruleInterface
147      * @return instance of the businessRulesClass for the given document's type, if that businessRulesClass implements
148      *         the given ruleInterface
149      */
150     public BusinessRule getBusinessRulesInstance(Document document, Class<? extends BusinessRule> ruleInterface) {
151         // get the businessRulesClass
152         Class<? extends BusinessRule> businessRulesClass = null;
153         if (document instanceof TransactionalDocument) {
154             TransactionalDocument transactionalDocument = (TransactionalDocument) document;
155 
156             businessRulesClass = getDocumentDictionaryService().getBusinessRulesClass(transactionalDocument);
157             if (businessRulesClass == null) {
158                 return new TransactionalDocumentRuleBase(); // default to a generic rule that will enforce Required fields
159             }
160         } else if (document instanceof MaintenanceDocument) {
161             MaintenanceDocument maintenanceDocument = (MaintenanceDocument) document;
162 
163             businessRulesClass = getDocumentDictionaryService().getBusinessRulesClass(maintenanceDocument);
164             if (businessRulesClass == null) {
165                 return new MaintenanceDocumentRuleBase(); // default to a generic rule that will enforce Required fields
166             }
167         } else {
168             LOG.error("unable to get businessRulesClass for unknown document type '"
169                     + document.getClass().getName()
170                     + "'");
171         }
172 
173         // instantiate and return it if it implements the given ruleInterface
174         BusinessRule rule = null;
175         if (businessRulesClass != null) {
176             try {
177                 if (ruleInterface.isAssignableFrom(businessRulesClass)) {
178                     rule = businessRulesClass.newInstance();
179                 }
180             } catch (IllegalAccessException e) {
181                 throw new RiceRuntimeException("error processing business rules", e);
182             } catch (InstantiationException e) {
183                 throw new RiceRuntimeException("error processing business rules", e);
184             }
185         }
186 
187         return rule;
188     }
189 
190     /**
191      * Increases the registered error path, so that field highlighting can occur on the appropriate object
192      * attribute
193      *
194      * @param errorPathPrefix
195      */
196     private void increaseErrorPath(String errorPathPrefix) {
197         MessageMap errorMap = GlobalVariables.getMessageMap();
198 
199         if (!StringUtils.isBlank(errorPathPrefix)) {
200             errorMap.addToErrorPath(errorPathPrefix);
201         }
202     }
203 
204     /**
205      * Decreases the registered error path, so that field highlighting can occur on the appropriate object
206      * attribute
207      *
208      * @param errorPathPrefix
209      */
210     private void decreaseErrorPath(String errorPathPrefix) {
211         MessageMap errorMap = GlobalVariables.getMessageMap();
212 
213         if (!StringUtils.isBlank(errorPathPrefix)) {
214             errorMap.removeFromErrorPath(errorPathPrefix);
215         }
216     }
217 
218     public DocumentDictionaryService getDocumentDictionaryService() {
219         if (documentDictionaryService == null) {
220             this.documentDictionaryService = KRADServiceLocatorWeb.getDocumentDictionaryService();
221         }
222         return documentDictionaryService;
223     }
224 
225     public void setDocumentDictionaryService(DocumentDictionaryService documentDictionaryService) {
226         this.documentDictionaryService = documentDictionaryService;
227     }
228 
229     public DataDictionaryService getDataDictionaryService() {
230         return dataDictionaryService;
231     }
232 
233     public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
234         this.dataDictionaryService = dataDictionaryService;
235     }
236 }