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.krms.impl.repository;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.criteria.CriteriaLookupService;
20  import org.kuali.rice.core.api.criteria.GenericQueryResults;
21  import org.kuali.rice.core.api.criteria.Predicate;
22  import org.kuali.rice.core.api.criteria.QueryByCriteria;
23  import org.kuali.rice.krad.service.BusinessObjectService;
24  import org.kuali.rice.krad.service.KRADServiceLocator;
25  import org.kuali.rice.krms.api.repository.RuleRepositoryService;
26  import org.kuali.rice.krms.api.repository.agenda.AgendaTreeDefinition;
27  import org.kuali.rice.krms.api.repository.agenda.AgendaTreeRuleEntry;
28  import org.kuali.rice.krms.api.repository.agenda.AgendaTreeSubAgendaEntry;
29  import org.kuali.rice.krms.api.repository.context.ContextDefinition;
30  import org.kuali.rice.krms.api.repository.context.ContextSelectionCriteria;
31  import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
32  import org.kuali.rice.krms.impl.util.KrmsImplConstants.PropertyNames;
33  
34  import java.util.ArrayList;
35  import java.util.Collections;
36  import java.util.List;
37  import java.util.Map;
38  
39  import static org.kuali.rice.core.api.criteria.PredicateFactory.*;
40  
41  /**
42   * NOTE: This is a patched version of this class which fixes a bug in Rice 2.0.0 releated to execution of
43   * an agenda with no rules.
44   */
45  public class RuleRepositoryServiceImpl implements RuleRepositoryService {
46      protected BusinessObjectService businessObjectService;
47      private CriteriaLookupService criteriaLookupService;
48  	
49  	/**
50  	 * This overridden method ...
51  	 * 
52  	 * @see org.kuali.rice.krms.api.repository.RuleRepositoryService#selectContext(org.kuali.rice.krms.api.repository.context.ContextSelectionCriteria)
53  	 */
54      @Override
55      public ContextDefinition selectContext(
56      		ContextSelectionCriteria contextSelectionCriteria) {
57      	if (contextSelectionCriteria == null){
58      		throw new IllegalArgumentException("selection criteria is null");
59      	}
60      	if (StringUtils.isBlank(contextSelectionCriteria.getNamespaceCode())){
61      		throw new IllegalArgumentException("selection criteria namespaceCode is null or blank");
62      	}
63      	QueryByCriteria queryCriteria = buildQuery(contextSelectionCriteria);
64          GenericQueryResults<ContextBo> results = getCriteriaLookupService().lookup(ContextBo.class, queryCriteria);
65  
66      	List<ContextBo> resultBos = results.getResults();
67  
68      	//assuming 1 ?
69      	ContextDefinition result = null;
70      	if (resultBos != null) {
71      		if (resultBos.size() == 1) {
72      			ContextBo bo = resultBos.iterator().next();
73      			return ContextBo.to(bo);
74      		}
75      		else throw new IllegalArgumentException("ambiguous qualifiers");
76      	}
77      	return result;
78      }
79  
80  	@Override
81  	public AgendaTreeDefinition getAgendaTree(String agendaId) {
82  		if (StringUtils.isBlank(agendaId)){
83      		throw new IllegalArgumentException("agenda id is null or blank");
84      	}
85  		// Get agenda items from db, then build up agenda tree structure
86  		AgendaBo agendaBo = getBusinessObjectService().findBySinglePrimaryKey(AgendaBo.class, agendaId);
87  		String agendaItemId = agendaBo.getFirstItemId();
88  		
89  		// walk thru the agenda items, building an agenda tree definition Builder along the way
90  		AgendaTreeDefinition.Builder myBuilder = AgendaTreeDefinition.Builder.create();
91  		myBuilder.setAgendaId( agendaId );
92  		
93  		if (agendaItemId != null) {
94  			myBuilder = walkAgendaItemTree(agendaItemId, myBuilder);
95  		}
96  		
97  		// build the agenda tree and return it
98  		return myBuilder.build();
99  	}
100 	
101 	@Override
102 	public List<AgendaTreeDefinition> getAgendaTrees(List<String> agendaIds) {
103 		List<AgendaTreeDefinition> agendaTrees = new ArrayList<AgendaTreeDefinition>();
104 		for (String agendaId : agendaIds){
105 			agendaTrees.add( getAgendaTree(agendaId) );
106 		}
107         return Collections.unmodifiableList(agendaTrees);		
108 	}
109 	
110 	@Override
111 	public RuleDefinition getRule(String ruleId) {
112 		if (StringUtils.isBlank(ruleId)){
113 			return null;			
114 		}
115 		RuleBo bo = getBusinessObjectService().findBySinglePrimaryKey(RuleBo.class, ruleId);
116 		return RuleBo.to(bo);
117 	}
118 	
119 	@Override
120 	public List<RuleDefinition> getRules(List<String> ruleIds) {
121         if (ruleIds == null) throw new IllegalArgumentException("ruleIds must not be null");
122 
123         // Fetch BOs
124         List<RuleBo> bos = null;
125         if (ruleIds.size() == 0) {
126             bos = Collections.emptyList();
127         } else {
128             QueryByCriteria.Builder qBuilder = QueryByCriteria.Builder.create();
129             List<Predicate> pList = new ArrayList<Predicate>();
130             qBuilder.setPredicates(in("id", ruleIds.toArray()));
131             GenericQueryResults<RuleBo> results = getCriteriaLookupService().lookup(RuleBo.class, qBuilder.build());
132 
133     	    bos = results.getResults();
134     	}
135 
136         // Translate BOs
137         ArrayList<RuleDefinition> rules = new ArrayList<RuleDefinition>();
138         for (RuleBo bo : bos) {
139             RuleDefinition rule = RuleBo.to(bo);
140             rules.add(rule);
141         }
142         return Collections.unmodifiableList(rules);
143 	}
144 
145 	/**
146 	 * Recursive method to create AgendaTreeDefinition builder
147 	 * 	
148 	 *  
149 	 */
150 	private AgendaTreeDefinition.Builder walkAgendaItemTree(String agendaItemId, AgendaTreeDefinition.Builder builder){
151 		//TODO: prevent circular, endless looping
152 		if (StringUtils.isBlank(agendaItemId)){
153 			return null;
154 		}
155 		// Get AgendaItemDefinition Business Object from database
156 		// NOTE: if we read agendaItem one at a time from db.   Could consider linking in OJB and getting all at once
157 		AgendaItemBo agendaItemBo = getBusinessObjectService().findBySinglePrimaryKey(AgendaItemBo.class, agendaItemId);
158 		
159 		// If Rule  
160 		// TODO: validate that only either rule or subagenda, not both
161 		if (!StringUtils.isBlank( agendaItemBo.getRuleId() )){
162 			// setup new rule entry builder
163 			AgendaTreeRuleEntry.Builder ruleEntryBuilder = AgendaTreeRuleEntry.Builder
164 					.create(agendaItemBo.getId(), agendaItemBo.getRuleId());
165 			ruleEntryBuilder.setRuleId( agendaItemBo.getRuleId() );
166 			ruleEntryBuilder.setAgendaItemId( agendaItemBo.getId() );
167 			if (agendaItemBo.getWhenTrueId() != null){
168 				// Go follow the true branch, creating AgendaTreeDefinintion Builder for the
169 				// true branch level
170 				AgendaTreeDefinition.Builder myBuilder = AgendaTreeDefinition.Builder.create();
171 				myBuilder.setAgendaId( agendaItemBo.getAgendaId() );
172 				ruleEntryBuilder.setIfTrue( walkAgendaItemTree(agendaItemBo.getWhenTrueId(),myBuilder));
173 			}
174 			if (agendaItemBo.getWhenFalseId() != null){
175 				// Go follow the false branch, creating AgendaTreeDefinintion Builder 
176 				AgendaTreeDefinition.Builder myBuilder = AgendaTreeDefinition.Builder.create();
177 				myBuilder.setAgendaId( agendaItemBo.getAgendaId() );
178 				ruleEntryBuilder.setIfFalse( walkAgendaItemTree(agendaItemBo.getWhenFalseId(), myBuilder));
179 			}
180 			// Build the Rule Entry and add it to the AgendaTreeDefinition builder
181 			builder.addRuleEntry( ruleEntryBuilder.build() );
182 		}
183 		// if SubAgenda and a sub agenda tree entry
184 		if (!StringUtils.isBlank(agendaItemBo.getSubAgendaId())) {
185 			AgendaTreeSubAgendaEntry.Builder subAgendaEntryBuilder = 
186 				AgendaTreeSubAgendaEntry.Builder.create(agendaItemBo.getId(), agendaItemBo.getSubAgendaId());
187 			builder.addSubAgendaEntry( subAgendaEntryBuilder.build() );
188 			}
189 
190 		// if this agenda item has an "After Id", follow that branch
191 		if (!StringUtils.isBlank( agendaItemBo.getAlwaysId() )){
192 			builder = walkAgendaItemTree( agendaItemBo.getAlwaysId(), builder);
193 			
194 		}
195 		return builder;
196 	}
197 	
198 	/**
199 	 * 
200 	 * This method converts a {@link ContextSelectionCriteria} object into a
201 	 * {@link QueryByCriteria} object with the proper predicates for AttributeBo properties.
202 	 * 
203 	 * @param selectionCriteria
204 	 * @return 
205 	 */
206 	private QueryByCriteria buildQuery( ContextSelectionCriteria selectionCriteria ){
207 		Predicate p;
208 		QueryByCriteria.Builder qBuilder = QueryByCriteria.Builder.create();
209     	List<Predicate> pList = new ArrayList<Predicate>();
210     	if (selectionCriteria.getNamespaceCode() != null){
211     		p = equal(PropertyNames.Context.NAMESPACE, selectionCriteria.getNamespaceCode());
212     		pList.add(p);
213     	}
214     	if (selectionCriteria.getName() != null){
215     		p = equal(PropertyNames.Context.NAME, selectionCriteria.getName());
216     		pList.add(p);
217     	}
218     	if (selectionCriteria.getContextQualifiers() != null){
219     		for (Map.Entry<String, String> entry : selectionCriteria.getContextQualifiers().entrySet()){
220     			p = and(equal(PropertyNames.Context.ATTRIBUTE_BOS
221     					+ "." + PropertyNames.BaseAttribute.ATTRIBUTE_DEFINITION
222     					+ "." + PropertyNames.KrmsAttributeDefinition.NAME, entry.getKey()),
223     				equal(PropertyNames.Context.ATTRIBUTE_BOS
224     					+ "." + PropertyNames.BaseAttribute.VALUE, entry.getValue()));
225     			pList.add(p);
226     		}
227     	}
228      	Predicate[] preds = new Predicate[pList.size()];
229      	pList.toArray(preds);
230     	qBuilder.setPredicates(and(preds)); 
231 		return qBuilder.build();
232 	}
233 
234 	/**
235      * Sets the businessObjectService property.
236      *
237      * @param businessObjectService The businessObjectService to set.
238      */
239     public void setBusinessObjectService(final BusinessObjectService businessObjectService) {
240         this.businessObjectService = businessObjectService;
241     }
242 
243     protected BusinessObjectService getBusinessObjectService() {
244 		if ( businessObjectService == null ) {
245             // TODO: inject this instead
246 			businessObjectService = KRADServiceLocator.getBusinessObjectService();
247 		}
248 		return businessObjectService;
249 	}
250     
251     /**
252      * Sets the criteriaLookupService attribute value.
253      *
254      * @param criteriaLookupService The criteriaLookupService to set.
255      */
256     public void setCriteriaLookupService(final CriteriaLookupService criteriaLookupService) {
257         this.criteriaLookupService = criteriaLookupService;
258     }
259 
260     protected CriteriaLookupService getCriteriaLookupService() {
261         return criteriaLookupService;
262     }
263     
264 }