View Javadoc

1   /**
2    * Copyright 2005-2012 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.krad.bo.AdHocRoutePerson;
24  import org.kuali.rice.krad.bo.AdHocRouteWorkgroup;
25  import org.kuali.rice.krad.document.Document;
26  import org.kuali.rice.krad.maintenance.MaintenanceDocument;
27  import org.kuali.rice.krad.document.TransactionalDocument;
28  import org.kuali.rice.krad.exception.InfrastructureException;
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.DictionaryValidationService;
37  import org.kuali.rice.krad.service.DocumentDictionaryService;
38  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
39  import org.kuali.rice.krad.service.KualiRuleService;
40  import org.kuali.rice.krad.util.GlobalVariables;
41  import org.kuali.rice.krad.util.KRADConstants;
42  import org.kuali.rice.krad.util.MessageMap;
43  
44  /**
45   * This class represents a rule evaluator for Kuali. This class is to be used for evaluating business rule checks. The class defines
46   * one method right now - applyRules() which takes in a Document and a DocumentEvent and does the proper business rule checks based
47   * on the context of the event and the document type.
48   */
49  public class KualiRuleServiceImpl implements KualiRuleService {
50      private static final Logger LOG = Logger.getLogger(KualiRuleServiceImpl.class);
51  
52      private DocumentDictionaryService documentDictionaryService;
53      private DictionaryValidationService dictionaryValidationService;
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              }
95              else {
96              	if ( LOG.isDebugEnabled() ) {
97              		LOG.debug("processed " + event.getName() + " for rule " + rule.getClass().getName());
98              	}
99              }
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 }