View Javadoc
1   package org.kuali.rice.krms.impl.repository;
2   
3   import org.apache.commons.lang.StringUtils;
4   import org.kuali.rice.core.api.criteria.Predicate;
5   import org.kuali.rice.core.api.criteria.QueryByCriteria;
6   import org.kuali.rice.core.api.criteria.QueryResults;
7   import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
8   import org.kuali.rice.core.api.exception.RiceIllegalStateException;
9   import org.kuali.rice.core.api.mo.ModelObjectUtils;
10  import org.kuali.rice.krad.data.DataObjectService;
11  import org.kuali.rice.krad.data.PersistenceOption;
12  import org.kuali.rice.krad.service.KRADServiceLocator;
13  import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
14  import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
15  import org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition;
16  import org.springframework.util.CollectionUtils;
17  
18  import java.util.ArrayList;
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import static org.kuali.rice.core.api.criteria.PredicateFactory.in;
28  import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.deleteMatching;
29  import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.findMatching;
30  import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.findSingleMatching;
31  
32  /**
33   * Created by SW Genis on 2014/05/06.
34   */
35  public class KSAgendaBoServiceImpl implements AgendaBoService {
36  
37      // TODO: deal with active flag
38  
39      private DataObjectService dataObjectService;
40      private KrmsAttributeDefinitionService attributeDefinitionService;
41  
42      // used for converting lists of BOs to model objects
43      private static final ModelObjectUtils.Transformer<AgendaItemBo, AgendaItemDefinition> toAgendaItemDefinition =
44              new ModelObjectUtils.Transformer<AgendaItemBo, AgendaItemDefinition>() {
45                  public AgendaItemDefinition transform(AgendaItemBo input) {
46                      return AgendaItemBo.to(input);
47                  };
48              };
49  
50      // used for converting lists of BOs to model objects
51      private static final ModelObjectUtils.Transformer<AgendaBo, AgendaDefinition> toAgendaDefinition =
52              new ModelObjectUtils.Transformer<AgendaBo, AgendaDefinition>() {
53                  public AgendaDefinition transform(AgendaBo input) {
54                      return AgendaBo.to(input);
55                  };
56              };
57  
58  
59      /**
60       * This overridden method creates a KRMS Agenda in the repository
61       */
62      @Override
63      public AgendaDefinition createAgenda(AgendaDefinition agenda) {
64          if (agenda == null){
65              throw new RiceIllegalArgumentException("agenda is null");
66          }
67          final String nameKey = agenda.getName();
68          final String contextId = agenda.getContextId();
69          final AgendaDefinition existing = getAgendaByNameAndContextId(nameKey, contextId);
70          if (existing != null){
71              throw new IllegalStateException("the agenda to create already exists: " + agenda);
72          }
73  
74          AgendaBo agendaBo = from(agenda);
75          agendaBo = dataObjectService.save(agendaBo, PersistenceOption.FLUSH);
76          return to(agendaBo);
77      }
78  
79      /**
80       * This overridden method updates an existing Agenda in the repository
81       */
82      @Override
83      public void updateAgenda(AgendaDefinition agenda) {
84          if (agenda == null){
85              throw new RiceIllegalArgumentException("agenda is null");
86          }
87  
88          // must already exist to be able to update
89          final String agendaIdKey = agenda.getId();
90          final AgendaBo existing = dataObjectService.find(AgendaBo.class, agendaIdKey);
91          if (existing == null) {
92              throw new IllegalStateException("the agenda does not exist: " + agenda);
93          }
94          final AgendaDefinition toUpdate;
95          if (existing.getId().equals(agenda.getId())) {
96              toUpdate = agenda;
97          } else {
98              // if passed in id does not match existing id, correct it
99              final AgendaDefinition.Builder builder = AgendaDefinition.Builder.create(agenda);
100             builder.setId(existing.getId());
101             toUpdate = builder.build();
102         }
103 
104         // copy all updateable fields to bo
105         AgendaBo boToUpdate = from(toUpdate);
106 
107         // move over AgendaBo members that don't get populated from AgendaDefinition
108         boToUpdate.setItems(existing.getItems());
109 
110         // delete any old, existing attributes
111         Map<String,String> fields = new HashMap<String,String>(1);
112         fields.put("agenda.id", toUpdate.getId());
113         deleteMatching(dataObjectService, AgendaAttributeBo.class, fields);
114 
115         // update new agenda and create new attributes
116         dataObjectService.save(boToUpdate, PersistenceOption.FLUSH);
117     }
118 
119     @Override
120     public void deleteAgenda(String agendaId) {
121         if (agendaId == null){ throw new RiceIllegalArgumentException("agendaId is null"); }
122         final AgendaBo bo = dataObjectService.find(AgendaBo.class, agendaId);
123         if (bo == null){ throw new IllegalStateException("the Agenda to delete does not exists: " + agendaId);}
124 
125         List<AgendaItemDefinition> agendaItems = this.getAgendaItemsByAgendaId(bo.getId());
126         for( AgendaItemDefinition agendaItem : agendaItems) {
127             dataObjectService.delete(AgendaItemBo.from(agendaItem));
128         }
129 
130         dataObjectService.delete(bo);
131     }
132 
133     /**
134      * This overridden method retrieves an Agenda from the repository
135      */
136     @Override
137     public AgendaDefinition getAgendaByAgendaId(String agendaId) {
138         if (StringUtils.isBlank(agendaId)){
139             throw new RiceIllegalArgumentException("agenda id is null or blank");
140         }
141         AgendaBo bo = dataObjectService.find(AgendaBo.class, agendaId);
142         return to(bo);
143     }
144 
145     /**
146      * This overridden method retrieves an agenda from the repository
147      */
148     @Override
149     public AgendaDefinition getAgendaByNameAndContextId(String name, String contextId) {
150         if (StringUtils.isBlank(name)) {
151             throw new RiceIllegalArgumentException("name is blank");
152         }
153         if (StringUtils.isBlank(contextId)) {
154             throw new RiceIllegalArgumentException("contextId is blank");
155         }
156 
157         final Map<String, Object> map = new HashMap<String, Object>();
158         map.put("name", name);
159         map.put("contextId", contextId);
160 
161         AgendaBo myAgenda = findSingleMatching(dataObjectService, AgendaBo.class, map);
162         return to(myAgenda);
163     }
164 
165     /**
166      * This overridden method retrieves a set of agendas from the repository
167      */
168     @Override
169     public List<AgendaDefinition> getAgendasByContextId(String contextId) {
170         if (StringUtils.isBlank(contextId)){
171             throw new RiceIllegalArgumentException("context ID is null or blank");
172         }
173         final Map<String, Object> map = new HashMap<String, Object>();
174         map.put("contextId", contextId);
175         List<AgendaBo> bos = findMatching(dataObjectService, AgendaBo.class, map);
176 
177         return convertAgendaBosToImmutables(bos);
178     }
179 
180     /**
181      * This overridden method creates a new Agenda in the repository
182      */
183     @Override
184     public AgendaItemDefinition createAgendaItem(AgendaItemDefinition agendaItem) {
185         if (agendaItem == null){
186             throw new RiceIllegalArgumentException("agendaItem is null");
187         }
188         if (agendaItem.getId() != null){
189             final AgendaDefinition existing = getAgendaByAgendaId(agendaItem.getId());
190             if (existing != null){
191                 throw new IllegalStateException("the agendaItem to create already exists: " + agendaItem);
192             }
193         }
194 
195         AgendaItemBo bo = AgendaItemBo.from(agendaItem);
196         bo = dataObjectService.save(bo, PersistenceOption.FLUSH);
197         return AgendaItemBo.to(bo);
198     }
199 
200     /**
201      * This overridden method updates an existing Agenda in the repository
202      */
203     @Override
204     public void updateAgendaItem(AgendaItemDefinition agendaItem) {
205         if (agendaItem == null){
206             throw new RiceIllegalArgumentException("agendaItem is null");
207         }
208         final String agendaItemIdKey = agendaItem.getId();
209         final AgendaItemDefinition existing = getAgendaItemById(agendaItemIdKey);
210         if (existing == null) {
211             throw new IllegalStateException("the agenda item does not exist: " + agendaItem);
212         }
213         final AgendaItemDefinition toUpdate;
214         if (existing.getId().equals(agendaItem.getId())) {
215             toUpdate = agendaItem;
216         } else {
217             final AgendaItemDefinition.Builder builder = AgendaItemDefinition.Builder.create(agendaItem);
218             builder.setId(existing.getId());
219             toUpdate = builder.build();
220         }
221 
222         AgendaItemBo aiBo = AgendaItemBo.from(toUpdate);
223         //updateActionAttributes(aiBo);    KSENROLL-12750 - Don't think this is required with jpa.
224         dataObjectService.save(aiBo, PersistenceOption.FLUSH);
225     }
226 
227     // KSENROLL-12750 - Don't think this is required with jpa.
228     /*private void updateActionAttributes(AgendaItemBo aiBo) {
229         if(aiBo.getRule()!=null){
230             updateActionAttributes(aiBo.getRule().getActions());
231         }
232         if(aiBo.getWhenTrue()!=null){
233             updateActionAttributes(aiBo.getWhenTrue());
234         }
235         if(aiBo.getWhenFalse()!=null){
236             updateActionAttributes(aiBo.getWhenFalse());
237         }
238         if(aiBo.getAlways()!=null){
239             updateActionAttributes(aiBo.getAlways());
240         }
241     }
242 
243     private void updateActionAttributes(List<ActionBo> actionBos) {
244         for (ActionBo action : actionBos) {
245             for (ActionAttributeBo aa : action.getAttributeBos()) {
246                 Map<String, Object> map = new HashMap<String, Object>();
247                 map.put("actionId", action.getId());
248                 Collection<ActionAttributeBo> aaBos = findMatching(dataObjectService, ActionAttributeBo.class, map);
249 
250                 for (ActionAttributeBo aaBo : aaBos) {
251                     if (StringUtils.equals(aaBo.getAttributeDefinitionId(), aa.getAttributeDefinitionId())) {
252                         aa.setId(aaBo.getId());
253                         aa.setVersionNumber(aaBo.getVersionNumber());
254                     }
255                 }
256             }
257         }
258     }*/
259 
260     /**
261      * This overridden method adds a new AgendaItemDefinition to the repository
262      */
263     @Override
264     public void addAgendaItem(AgendaItemDefinition agendaItem, String parentId, Boolean position) {
265         if (agendaItem == null){
266             throw new RiceIllegalArgumentException("agendaItem is null");
267         }
268         AgendaItemDefinition parent = null;
269         if (parentId != null){
270             parent = getAgendaItemById(parentId);
271             if (parent == null){
272                 throw new IllegalStateException("parent agendaItem does not exist in repository. parentId = " + parentId);
273             }
274         }
275         // create new AgendaItemDefinition
276         final AgendaItemDefinition toCreate;
277         if (agendaItem.getId() == null) {
278             final AgendaItemDefinition.Builder builder = AgendaItemDefinition.Builder.create(agendaItem);
279             builder.setId(AgendaItemBo.agendaItemIdIncrementer.getNewId());
280             toCreate = builder.build();
281         } else {
282             toCreate = agendaItem;
283         }
284         createAgendaItem(toCreate);
285 
286         // link it to it's parent (for whenTrue/whenFalse, sibling for always
287         if (parentId != null) {
288             final AgendaItemDefinition.Builder builder = AgendaItemDefinition.Builder.create(parent);
289             if (position == null){
290                 builder.setAlwaysId( toCreate.getId() );
291             } else if (position.booleanValue()){
292                 builder.setWhenTrueId( toCreate.getId() );
293             } else if (!position.booleanValue()){
294                 builder.setWhenFalseId( toCreate.getId() );
295             }
296             final AgendaItemDefinition parentToUpdate = builder.build();
297             updateAgendaItem( parentToUpdate );
298         }
299     }
300 
301     /**
302      * This overridden method retrieves an AgendaItemDefinition from the repository
303      */
304     @Override
305     public AgendaItemDefinition getAgendaItemById(String id) {
306         if (StringUtils.isBlank(id)){
307             throw new RiceIllegalArgumentException("agenda item id is null or blank");
308         }
309 
310         AgendaItemBo bo = dataObjectService.find(AgendaItemBo.class, id);
311 
312         return AgendaItemBo.to(bo);
313     }
314 
315     @Override
316     public List<AgendaItemDefinition> getAgendaItemsByAgendaId(String agendaId) {
317         if (StringUtils.isBlank(agendaId)){
318             throw new RiceIllegalArgumentException("agenda id is null or null");
319         }
320         List<AgendaItemDefinition> results = null;
321 
322         Collection<AgendaItemBo> bos = findMatching(dataObjectService, AgendaItemBo.class, Collections.singletonMap(
323                 "agendaId", agendaId));
324 
325         if (CollectionUtils.isEmpty(bos)) {
326             results = Collections.emptyList();
327         } else {
328             results = Collections.unmodifiableList(ModelObjectUtils.transform(bos, toAgendaItemDefinition));
329         }
330 
331         return results;
332     }
333 
334     @Override
335     public List<AgendaDefinition> getAgendasByType(String typeId) throws RiceIllegalArgumentException {
336         if (StringUtils.isBlank(typeId)){
337             throw new RiceIllegalArgumentException("type ID is null or blank");
338         }
339 
340         final Map<String, Object> map = new HashMap<String, Object>();
341         map.put("typeId", typeId);
342         List<AgendaBo> bos = findMatching(dataObjectService, AgendaBo.class, map);
343 
344         return convertAgendaBosToImmutables(bos);
345     }
346 
347     @Override
348     public List<AgendaDefinition> getAgendasByTypeAndContext(String typeId,
349                                                              String contextId) throws RiceIllegalArgumentException {
350         if (StringUtils.isBlank(typeId)){
351             throw new RiceIllegalArgumentException("type ID is null or blank");
352         }
353         if (StringUtils.isBlank(contextId)){
354             throw new RiceIllegalArgumentException("context ID is null or blank");
355         }
356         final Map<String, Object> map = new HashMap<String, Object>();
357         map.put("typeId", typeId);
358         map.put("contextId", contextId);
359         Collection<AgendaBo> bos = findMatching(dataObjectService, AgendaBo.class, map);
360 
361         return convertAgendaBosToImmutables(bos);
362     }
363 
364     @Override
365     public List<AgendaItemDefinition> getAgendaItemsByType(String typeId) throws RiceIllegalArgumentException {
366         return findAgendaItemsForAgendas(getAgendasByType(typeId));
367     }
368 
369     @Override
370     public List<AgendaItemDefinition> getAgendaItemsByContext(String contextId) throws RiceIllegalArgumentException {
371         return findAgendaItemsForAgendas(getAgendasByContextId(contextId));
372     }
373 
374     @Override
375     public List<AgendaItemDefinition> getAgendaItemsByTypeAndContext(String typeId,
376                                                                      String contextId) throws RiceIllegalArgumentException {
377         return findAgendaItemsForAgendas(getAgendasByTypeAndContext(typeId, contextId));
378     }
379 
380     @Override
381     public void deleteAgendaItem(String agendaItemId) throws RiceIllegalArgumentException {
382         if (StringUtils.isBlank(agendaItemId)) {
383             throw new RiceIllegalArgumentException("agendaItemId must not be blank or null");
384         }
385 
386         deleteMatching(dataObjectService, AgendaItemBo.class, Collections.singletonMap("id", agendaItemId));
387     }
388 
389     private List<AgendaItemDefinition> findAgendaItemsForAgendas(List<AgendaDefinition> agendaDefinitions) {
390         List<AgendaItemDefinition> results = null;
391 
392         if (!CollectionUtils.isEmpty(agendaDefinitions)) {
393             List<AgendaItemBo> boResults = new ArrayList<AgendaItemBo>(agendaDefinitions.size());
394 
395             List<String> agendaIds = new ArrayList<String>(20);
396             for (AgendaDefinition agendaDefinition : agendaDefinitions) {
397                 agendaIds.add(agendaDefinition.getId());
398 
399                 if (agendaIds.size() == 20) {
400                     // fetch batch
401 
402                     Predicate predicate = in("agendaId", agendaIds.toArray());
403                     QueryByCriteria criteria = QueryByCriteria.Builder.fromPredicates(predicate);
404                     QueryResults<AgendaItemBo> batch = getDataObjectService().findMatching(AgendaItemBo.class, criteria);
405 
406                     boResults.addAll(batch.getResults());
407 
408                     // reset agendaIds
409                     agendaIds.clear();
410                 }
411             }
412 
413             if (agendaIds.size() > 0) {
414                 Predicate predicate = in("agendaId", agendaIds.toArray());
415                 QueryByCriteria criteria = QueryByCriteria.Builder.fromPredicates(predicate);
416                 QueryResults<AgendaItemBo> batch = getDataObjectService().findMatching(AgendaItemBo.class, criteria);
417 
418                 boResults.addAll(batch.getResults());
419             }
420 
421             results = Collections.unmodifiableList(ModelObjectUtils.transform(boResults, toAgendaItemDefinition));
422         } else {
423             results = Collections.emptyList();
424         }
425 
426         return results;
427     }
428 
429     /**
430      * Sets the dataObjectService attribute value.
431      *
432      * @param dataObjectService The dataObjectService to set.
433      */
434     public void setDataObjectService(final DataObjectService dataObjectService) {
435         this.dataObjectService = dataObjectService;
436     }
437 
438     protected DataObjectService getDataObjectService() {
439         if ( dataObjectService == null ) {
440             dataObjectService = KRADServiceLocator.getDataObjectService();
441         }
442         return dataObjectService;
443     }
444 
445     protected KrmsAttributeDefinitionService getAttributeDefinitionService() {
446         if (attributeDefinitionService == null) {
447             attributeDefinitionService = KrmsRepositoryServiceLocator.getKrmsAttributeDefinitionService();
448         }
449         return attributeDefinitionService;
450     }
451 
452     public void setAttributeDefinitionService(KrmsAttributeDefinitionService attributeDefinitionService) {
453         this.attributeDefinitionService = attributeDefinitionService;
454     }
455 
456     /**
457      * Converts a Set<AgendaBo> to an Unmodifiable Set<Agenda>
458      *
459      * @param agendaBos a mutable Set<AgendaBo> to made completely immutable.
460      * @return An unmodifiable Set<Agenda>
461      */
462     public List<AgendaDefinition> convertAgendaBosToImmutables(final Collection<AgendaBo> agendaBos) {
463         if (CollectionUtils.isEmpty(agendaBos)) {
464             return Collections.emptyList();
465         }
466         return Collections.unmodifiableList(ModelObjectUtils.transform(agendaBos, toAgendaDefinition));
467     }
468 
469     /**
470      * Converts a mutable bo to it's immutable counterpart
471      * @param bo the mutable business object
472      * @return the immutable object
473      */
474     @Override
475     public AgendaDefinition to(AgendaBo bo) {
476         if (bo == null) { return null; }
477         return org.kuali.rice.krms.api.repository.agenda.AgendaDefinition.Builder.create(bo).build();
478     }
479 
480 
481     /**
482      * Converts a immutable object to it's mutable bo counterpart
483      * @param im immutable object
484      * @return the mutable bo
485      */
486     @Override
487     public AgendaBo from(AgendaDefinition im) {
488         if (im == null) { return null; }
489 
490         AgendaBo bo = new AgendaBo();
491         bo.setId(im.getId());
492         bo.setName( im.getName() );
493         bo.setTypeId( im.getTypeId() );
494         bo.setContextId( im.getContextId() );
495         bo.setFirstItemId( im.getFirstItemId() );
496         bo.setVersionNumber( im.getVersionNumber() );
497         bo.setActive(im.isActive());
498         Set<AgendaAttributeBo> attributes = buildAgendaAttributeBo(im, bo);
499 
500         bo.setAttributeBos(attributes);
501 
502         return bo;
503     }
504 
505     private Set<AgendaAttributeBo> buildAgendaAttributeBo(AgendaDefinition im, AgendaBo agendaBo) {
506         Set<AgendaAttributeBo> attributes = new HashSet<AgendaAttributeBo>();
507 
508         // build a map from attribute name to definition
509         Map<String, KrmsAttributeDefinition> attributeDefinitionMap = new HashMap<String, KrmsAttributeDefinition>();
510 
511         List<KrmsAttributeDefinition> attributeDefinitions =
512                 getAttributeDefinitionService().findAttributeDefinitionsByType(im.getTypeId());
513 
514         for (KrmsAttributeDefinition attributeDefinition : attributeDefinitions) {
515             attributeDefinitionMap.put(attributeDefinition.getName(), attributeDefinition);
516         }
517 
518         // for each entry, build an AgendaAttributeBo and add it to the set
519         for (Map.Entry<String,String> entry  : im.getAttributes().entrySet()){
520             KrmsAttributeDefinition attrDef = attributeDefinitionMap.get(entry.getKey());
521 
522             if (attrDef != null) {
523                 AgendaAttributeBo attributeBo = new AgendaAttributeBo();
524                 attributeBo.setAgenda(agendaBo);
525                 attributeBo.setValue(entry.getValue());
526                 attributeBo.setAttributeDefinition(KrmsAttributeDefinitionBo.from(attrDef));
527                 attributes.add( attributeBo );
528             } else {
529                 throw new RiceIllegalStateException("there is no attribute definition with the name '" +
530                         entry.getKey() + "' that is valid for the agenda type with id = '" + im.getTypeId() +"'");
531             }
532         }
533         return attributes;
534     }
535 }