001/**
002 * Copyright 2005-2017 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 */
016package org.kuali.rice.kew.impl.rule;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.log4j.Logger;
020import org.joda.time.DateTime;
021import org.kuali.rice.core.api.criteria.Predicate;
022import org.kuali.rice.core.api.criteria.QueryByCriteria;
023import org.kuali.rice.core.api.criteria.QueryResults;
024import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
025import org.kuali.rice.core.api.exception.RiceIllegalStateException;
026import org.kuali.rice.kew.api.KewApiServiceLocator;
027import org.kuali.rice.kew.api.doctype.DocumentTypeService;
028import org.kuali.rice.kew.api.rule.Rule;
029import org.kuali.rice.kew.api.rule.RuleDelegation;
030import org.kuali.rice.kew.api.rule.RuleQueryResults;
031import org.kuali.rice.kew.api.rule.RuleReportCriteria;
032import org.kuali.rice.kew.api.rule.RuleResponsibility;
033import org.kuali.rice.kew.api.rule.RuleService;
034import org.kuali.rice.kew.api.rule.RuleTemplate;
035import org.kuali.rice.kew.api.rule.RuleTemplateQueryResults;
036import org.kuali.rice.kew.rule.RuleBaseValues;
037import org.kuali.rice.kew.rule.RuleDelegationBo;
038import org.kuali.rice.kew.rule.RuleResponsibilityBo;
039import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
040import org.kuali.rice.kew.service.KEWServiceLocator;
041import org.kuali.rice.kim.impl.common.attribute.AttributeTransform;
042import org.kuali.rice.krad.data.DataObjectService;
043
044import javax.jws.WebParam;
045import java.util.ArrayList;
046import java.util.Collection;
047import java.util.List;
048
049import static org.kuali.rice.core.api.criteria.PredicateFactory.*;
050
051
052/**
053 *
054 * @author Kuali Rice Team (rice.collab@kuali.org)
055 *
056 */
057public class RuleServiceImpl implements RuleService {
058
059    private static final Logger LOG = Logger.getLogger(RuleServiceImpl.class);
060
061    private DataObjectService dataObjectService;
062
063    @Override
064    public Rule getRule(String id) throws RiceIllegalArgumentException, RiceIllegalStateException{
065        incomingParamCheck("id", id);
066        RuleBaseValues rbv = getDataObjectService().find(RuleBaseValues.class, id);
067        if (rbv == null) {
068            throw new RiceIllegalStateException("Rule with specified id: " + id + " does not exist");
069        }
070        return RuleBaseValues.to(rbv);
071    }
072
073    @Override
074    public Rule getRuleByName(String name) {
075        incomingParamCheck("name", name);
076        QueryByCriteria.Builder builder = QueryByCriteria.Builder.create();
077        builder.setPredicates(
078                equal("name", name),
079                equal("currentInd", Boolean.TRUE)
080        );
081        QueryResults<RuleBaseValues> results = getDataObjectService().findMatching(RuleBaseValues.class, builder.build());
082        if (results.getResults().isEmpty()) {
083            throw new RiceIllegalStateException("Rule with specified name: " + name + " does not exist");
084        }
085        if (results.getResults().size() > 1) {
086            throw new RiceIllegalStateException("Found more than one current rule with specified name " + name);
087        }
088        return RuleBaseValues.to(results.getResults().get(0));
089    }
090
091    @Override
092    public List<Rule> getRulesByTemplateId(
093            @WebParam(name = "templateId") String templateId) throws RiceIllegalArgumentException {
094        incomingParamCheck("templateId", templateId);
095        QueryByCriteria.Builder builder = QueryByCriteria.Builder.create();
096        builder.setPredicates(equal("ruleTemplateId", templateId), equal("currentInd", Boolean.TRUE));
097        QueryResults<RuleBaseValues> results = getDataObjectService().findMatching(RuleBaseValues.class, builder.build());
098        final List<Rule> rules = new ArrayList<Rule>();
099        for (RuleBaseValues bo : results.getResults()) {
100            rules.add(Rule.Builder.create(bo).build());
101        }
102        return rules;
103    }
104
105    @Override
106    public List<Rule> getRulesByTemplateNameAndDocumentTypeName(String templateName, String documentTypeName) {
107        return getRulesByTemplateNameAndDocumentTypeNameAndEffectiveDate(templateName, documentTypeName, null);
108    }
109
110    @Override
111    public List<Rule> getRulesByTemplateNameAndDocumentTypeNameAndEffectiveDate(String templateName, String documentTypeName,
112            DateTime effectiveDate)
113            throws RiceIllegalArgumentException {
114        QueryByCriteria.Builder query = QueryByCriteria.Builder.create();
115        List<Predicate> predicates = new ArrayList<Predicate>();
116        predicates.add(equal("ruleTemplate.name", templateName));
117
118        // Check all document types in ancestry
119        DocumentTypeService documentTypeService = KewApiServiceLocator.getDocumentTypeService();
120        org.kuali.rice.kew.api.doctype.DocumentType dt = documentTypeService.getDocumentTypeByName(documentTypeName);
121        List<String> documentTypeAncestryNames = new ArrayList<String>();
122        while (dt != null) {
123            documentTypeAncestryNames.add(dt.getName());
124            dt = dt.getParentId() == null ? null : documentTypeService.getDocumentTypeById(dt.getParentId());
125        }
126        predicates.add(in("docTypeName", documentTypeAncestryNames.toArray(
127                new String[documentTypeAncestryNames.size()])));
128        DateTime currentTime = new DateTime();
129        predicates.add(and(
130                           or(isNull("fromDateValue"), lessThanOrEqual("fromDateValue", currentTime)),
131                           or(isNull("toDateValue"), greaterThan("toDateValue", currentTime))
132                      ));
133        predicates.add(equal("active", Boolean.TRUE));
134        predicates.add(equal("delegateRule", Boolean.FALSE));
135        predicates.add(equal("templateRuleInd", Boolean.FALSE));
136        if (effectiveDate != null) {
137            predicates.add(
138                    and(
139                        or(isNull("activationDate"), lessThanOrEqual("activationDate", effectiveDate)),
140                        or(isNull("deactivationDate"), greaterThan("deactivationDate", effectiveDate))
141                    ));
142        } else {
143            predicates.add(equal("currentInd", Boolean.TRUE));
144        }
145        Predicate p = and(predicates.toArray(new Predicate[]{}));
146        query.setPredicates(p);
147        return KewApiServiceLocator.getRuleService().findRules(query.build()).getResults();
148    }
149
150    @Override
151    public RuleQueryResults findRules(QueryByCriteria queryByCriteria) {
152        if (queryByCriteria == null) {
153            throw new RiceIllegalArgumentException("queryByCriteria is null");
154        }
155
156        QueryResults<RuleBaseValues> results = dataObjectService.findMatching(RuleBaseValues.class,
157                AttributeTransform.getInstance().apply(queryByCriteria));
158
159        RuleQueryResults.Builder builder = RuleQueryResults.Builder.create();
160        builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
161        builder.setTotalRowCount(results.getTotalRowCount());
162
163        final List<Rule.Builder> ims = new ArrayList<Rule.Builder>();
164        for (RuleBaseValues bo : results.getResults()) {
165            ims.add(Rule.Builder.create(RuleBaseValues.to(bo)));
166        }
167
168        builder.setResults(ims);
169        return builder.build();
170    }
171
172    @Override
173    public List<Rule> ruleReport(RuleReportCriteria ruleReportCriteria) {
174        incomingParamCheck(ruleReportCriteria, "ruleReportCriteria");
175        if ( LOG.isDebugEnabled() ) {
176                LOG.debug("Executing rule report [responsibleUser=" + ruleReportCriteria.getResponsiblePrincipalId() + ", responsibleWorkgroup=" +
177                    ruleReportCriteria.getResponsibleGroupId() + "]");
178        }
179        Collection<RuleBaseValues> rulesFound = KEWServiceLocator.getRuleService().searchByTemplate(
180                ruleReportCriteria.getDocumentTypeName(), ruleReportCriteria.getRuleTemplateName(),
181                ruleReportCriteria.getRuleDescription(), ruleReportCriteria.getResponsibleGroupId(),
182                ruleReportCriteria.getResponsiblePrincipalId(), Boolean.valueOf(ruleReportCriteria.isConsiderGroupMembership()),
183                Boolean.valueOf(ruleReportCriteria.isIncludeDelegations()), Boolean.valueOf(ruleReportCriteria.isActive()), ruleReportCriteria.getRuleExtensions(),
184                ruleReportCriteria.getActionRequestCodes());
185        List<org.kuali.rice.kew.api.rule.Rule> returnableRules = new ArrayList<Rule>(rulesFound.size());
186        for (RuleBaseValues rule : rulesFound) {
187            returnableRules.add(RuleBaseValues.to(rule));
188        }
189        return returnableRules;
190    }
191
192    @Override
193    public RuleTemplate getRuleTemplate(@WebParam(name = "id") String id) {
194        incomingParamCheck("id", id);
195        RuleTemplateBo template = dataObjectService.find(RuleTemplateBo.class, id);
196        if (template == null) {
197            throw new RiceIllegalStateException("RuleTemplate with specified id: " + id + " does not exist");
198        }
199        return RuleTemplateBo.to(template);
200    }
201
202    @Override
203    public RuleTemplate getRuleTemplateByName(@WebParam(name = "name") String name) {
204        incomingParamCheck("name", name);
205        QueryByCriteria.Builder builder = QueryByCriteria.Builder.create();
206        builder.setPredicates(equal("name", name));
207        QueryResults<RuleTemplateBo> results = dataObjectService.findMatching(RuleTemplateBo.class, builder.build());
208        if (results.getResults().isEmpty()) {
209            throw new RiceIllegalStateException("Rule Template with specified name: " + name + " does not exist");
210        }
211        if (results.getResults().size() > 1) {
212            throw new RiceIllegalStateException("Found more than one rule template with specified name " + name);
213        }
214        return RuleTemplateBo.to(results.getResults().get(0));
215    }
216
217    @Override
218    public RuleTemplateQueryResults findRuleTemplates(
219            @WebParam(name = "query") QueryByCriteria queryByCriteria) throws RiceIllegalArgumentException {
220        if (queryByCriteria == null) {
221            throw new RiceIllegalArgumentException("queryByCriteria is null");
222        }
223
224        QueryResults<RuleTemplateBo> results = dataObjectService.findMatching(RuleTemplateBo.class,
225                AttributeTransform.getInstance().apply(queryByCriteria));
226
227        RuleTemplateQueryResults.Builder builder = RuleTemplateQueryResults.Builder.create();
228        builder.setMoreResultsAvailable(results.isMoreResultsAvailable());
229        builder.setTotalRowCount(results.getTotalRowCount());
230
231        final List<RuleTemplate.Builder> ims = new ArrayList<RuleTemplate.Builder>();
232        for (RuleTemplateBo bo : results.getResults()) {
233            ims.add(RuleTemplate.Builder.create(RuleTemplateBo.to(bo)));
234        }
235
236        builder.setResults(ims);
237        return builder.build();
238    }
239
240    @Override
241    public RuleResponsibility getRuleResponsibility(String responsibilityId) {
242        incomingParamCheck("responsibilityId", responsibilityId);
243        QueryByCriteria.Builder builder = QueryByCriteria.Builder.create();
244        builder.setPredicates(equal("responsibilityId", responsibilityId));
245        QueryResults<RuleResponsibilityBo> results = dataObjectService.findMatching(RuleResponsibilityBo.class, builder.build());
246        if (results.getResults().isEmpty()) {
247            throw new RiceIllegalStateException("RuleResponsibility with specified id: " + responsibilityId + " does not exist");
248        }
249        if (results.getResults().size() > 1) {
250            throw new RiceIllegalStateException("Found more than one rule responsibility with responsibility id: " + responsibilityId);
251        }
252        return RuleResponsibilityBo.to(results.getResults().get(0));
253    }
254
255    @Override
256    public List<RuleDelegation> getRuleDelegationsByResponsibiltityId(
257            @WebParam(name = "id") String id) throws RiceIllegalArgumentException, RiceIllegalStateException {
258        incomingParamCheck("id", id);
259        QueryByCriteria.Builder builder = QueryByCriteria.Builder.create();
260        builder.setPredicates(
261                equal("responsibilityId", id),
262                equal("delegationRule.currentInd", Boolean.TRUE)
263        );
264        QueryResults<RuleDelegationBo> results = dataObjectService.findMatching(RuleDelegationBo.class, builder.build());
265        List<RuleDelegation> ruleDelegations = new ArrayList<RuleDelegation>();
266        for (RuleDelegationBo bo : results.getResults()) {
267            ruleDelegations.add(RuleDelegationBo.to(bo));
268        }
269        return ruleDelegations;
270    }
271
272    private void incomingParamCheck(Object object, String name) {
273        if (object == null) {
274            throw new RiceIllegalArgumentException(name + " was null");
275        } else if (object instanceof String
276                && StringUtils.isBlank((String) object)) {
277            throw new RiceIllegalArgumentException(name + " was blank");
278        }
279    }
280
281    public DataObjectService getDataObjectService() {
282        return dataObjectService;
283    }
284
285    public void setDataObjectService(DataObjectService dataObjectService) {
286        this.dataObjectService = dataObjectService;
287    }
288
289}