View Javadoc
1   /**
2    * Copyright 2005-2016 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.Predicate;
20  import org.kuali.rice.core.api.criteria.QueryByCriteria;
21  import org.kuali.rice.core.api.criteria.QueryResults;
22  import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
23  import org.kuali.rice.core.api.exception.RiceIllegalStateException;
24  import org.kuali.rice.core.api.mo.ModelObjectUtils;
25  import org.kuali.rice.krad.data.DataObjectService;
26  import org.kuali.rice.krad.data.PersistenceOption;
27  import org.kuali.rice.krad.service.KRADServiceLocator;
28  import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
29  import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
30  import org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition;
31  import org.springframework.util.CollectionUtils;
32  
33  import java.util.ArrayList;
34  import java.util.Collection;
35  import java.util.Collections;
36  import java.util.HashMap;
37  import java.util.HashSet;
38  import java.util.List;
39  import java.util.Map;
40  import java.util.Map.Entry;
41  import java.util.Set;
42  
43  import static org.kuali.rice.core.api.criteria.PredicateFactory.in;
44  import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.*;
45  
46  /**
47   * Implementation of the interface for accessing KRMS repository Agenda related
48   * business objects.
49   *
50   * @author Kuali Rice Team (rice.collab@kuali.org)
51   */
52  public class AgendaBoServiceImpl implements AgendaBoService {
53  
54      // TODO: deal with active flag
55  
56      // used for converting lists of BOs to model objects
57      private static final ModelObjectUtils.Transformer<AgendaItemBo, AgendaItemDefinition> toAgendaItemDefinition =
58              new ModelObjectUtils.Transformer<AgendaItemBo, AgendaItemDefinition>() {
59                  public AgendaItemDefinition transform(AgendaItemBo input) {
60                      return AgendaItemBo.to(input);
61                  };
62              };
63      // used for converting lists of BOs to model objects
64      private static final ModelObjectUtils.Transformer<AgendaBo, AgendaDefinition> toAgendaDefinition =
65              new ModelObjectUtils.Transformer<AgendaBo, AgendaDefinition>() {
66                  public AgendaDefinition transform(AgendaBo input) {
67                      return AgendaBo.to(input);
68                  };
69              };
70  
71      private DataObjectService dataObjectService;
72      private KrmsAttributeDefinitionService attributeDefinitionService;
73  
74      /**
75       * This overridden method creates a KRMS Agenda in the repository
76       */
77      @Override
78      public AgendaDefinition createAgenda(AgendaDefinition agenda) {
79          if (agenda == null) {
80              throw new RiceIllegalArgumentException("agenda is null");
81          }
82          final String nameKey = agenda.getName();
83          final String contextId = agenda.getContextId();
84          final AgendaDefinition existing = getAgendaByNameAndContextId(nameKey, contextId);
85          if (existing != null) {
86              throw new IllegalStateException("the agenda to create already exists: " + agenda);
87          }
88  
89          AgendaBo agendaBo = from(agenda);
90          agendaBo = getDataObjectService().save(agendaBo, PersistenceOption.FLUSH);
91          return to(agendaBo);
92      }
93  
94      /**
95       * This overridden method updates an existing Agenda in the repository
96       */
97      @Override
98      public AgendaDefinition updateAgenda(AgendaDefinition agendaDefinition) {
99          if (agendaDefinition == null) {
100             throw new RiceIllegalArgumentException("agenda is null");
101         }
102 
103         // must already exist to be able to update
104         final String agendaDefinitionId = agendaDefinition.getId();
105 
106         // All AgendaItemDefinitions for the specified Agenda ID will end up in the "items" property.
107         final AgendaBo agendaBoExisting = getDataObjectService().find(AgendaBo.class, agendaDefinitionId);
108         if (agendaBoExisting == null) {
109             throw new IllegalStateException("the agenda does not exist: " + agendaDefinition);
110         }
111 
112         final AgendaDefinition agendaDefinitionToUpdate;
113         if (agendaBoExisting.getId().equals(agendaDefinition.getId())) {
114             agendaDefinitionToUpdate = agendaDefinition;
115         } else {
116             // if passed in id does not match existing id, correct it
117             final AgendaDefinition.Builder builder = AgendaDefinition.Builder.create(agendaDefinition);
118             builder.setId(agendaBoExisting.getId());
119             agendaDefinitionToUpdate = builder.build();
120         }
121 
122         // copy all updateable fields to bo
123         AgendaBo agendaBoToUpdate = from(agendaDefinitionToUpdate);
124 
125         // move over AgendaBo members that don't get populated from AgendaDefinition
126         agendaBoToUpdate.setItems(agendaBoExisting.getItems());
127         if (StringUtils.isNotBlank(agendaDefinition.getFirstItemId())) {
128             agendaBoToUpdate.setFirstItem(getDataObjectService().find(AgendaItemBo.class, agendaDefinition.getFirstItemId()));
129         }
130 
131         // delete any old, existing attributes
132         Map<String, String> fields = new HashMap<String, String>(1);
133         fields.put("agenda.id", agendaDefinitionToUpdate.getId());
134         // deletes the record(s) in krms_agenda_attr_t specified by agenda_id col
135         deleteMatching(getDataObjectService(), AgendaAttributeBo.class, fields);
136 
137         // Will get used to determine which "previous" AgendaItems can get deleted
138         ArrayList<String> agendaItemsIds = new ArrayList<String>();
139 
140         // Will get used for a quick AgendaItem ID to AgendaItemBo lookup
141         HashMap<String, AgendaItemBo> mapIdToBo = new HashMap<String, AgendaItemBo>();
142 
143         // Get all AgendaItems which have the specified Agenda ID.  Make a "referenced" list as well.
144         ArrayList<String> agendaItemIdsReferenced = new ArrayList<String>();
145         List<AgendaItemBo> items = agendaBoToUpdate.getItems();
146         for (AgendaItemBo agendaItemBo : items) {
147             agendaItemsIds.add(agendaItemBo.getId());
148             mapIdToBo.put(agendaItemBo.getId(), agendaItemBo);
149 
150             if (agendaItemBo.getAlwaysId() != null) {
151                 agendaItemIdsReferenced.add(agendaItemBo.getAlwaysId());
152             }
153 
154             if (agendaItemBo.getWhenTrueId() != null) {
155                 agendaItemIdsReferenced.add(agendaItemBo.getWhenTrueId());
156             }
157 
158             if (agendaItemBo.getWhenFalseId() != null) {
159                 agendaItemIdsReferenced.add(agendaItemBo.getWhenFalseId());
160             }
161         }
162 
163         // update new agenda and create new attributes
164         AgendaBo agendaBoUpdated = getDataObjectService().save(agendaBoToUpdate, PersistenceOption.FLUSH);
165 
166         // Walk the "always", "whenTrue", and "whenFalse" lists.
167         ArrayList<String> agendaItemIdsActuallyUsed = new ArrayList<String>();
168         if (agendaBoUpdated.getFirstItem() != null) {
169             AgendaItemBo agendaItemBoTop = agendaBoUpdated.getFirstItem();
170 
171             AgendaItemBo agendaItemBoCurrent = agendaItemBoTop;
172             agendaItemIdsActuallyUsed.add(agendaItemBoCurrent.getId());
173 
174             // always list
175             while (agendaItemBoCurrent != null) {
176                 if (StringUtils.isNotEmpty(agendaItemBoCurrent.getAlwaysId())) {
177                     agendaItemIdsActuallyUsed.add(agendaItemBoCurrent.getAlwaysId());
178                 }
179 
180                 agendaItemBoCurrent = agendaItemBoCurrent.getAlways();
181             }
182 
183             // whenTrue list
184             agendaItemBoCurrent = agendaItemBoTop;
185             while (agendaItemBoCurrent != null) {
186                 if (StringUtils.isNotEmpty(agendaItemBoCurrent.getWhenTrueId())) {
187                     agendaItemIdsActuallyUsed.add(agendaItemBoCurrent.getWhenTrueId());
188                 }
189 
190                 agendaItemBoCurrent = agendaItemBoCurrent.getWhenTrue();
191             }
192 
193             // whenFalse list
194             agendaItemBoCurrent = agendaItemBoTop;
195             while (agendaItemBoCurrent != null) {
196                 if (StringUtils.isNotEmpty(agendaItemBoCurrent.getWhenFalseId())) {
197                     agendaItemIdsActuallyUsed.add(agendaItemBoCurrent.getWhenFalseId());
198                 }
199 
200                 agendaItemBoCurrent = agendaItemBoCurrent.getWhenFalse();
201             }
202         }
203 
204         // Compare what is used by the updated Agenda to all AgendaItem IDs for this Agenda ID
205         for (String sIdActuallyUsed : agendaItemIdsActuallyUsed) {
206             if (agendaItemsIds.contains(sIdActuallyUsed)) {
207                 agendaItemsIds.remove(sIdActuallyUsed);
208             }
209         }
210 
211         // Anything remaining is an orphan. Only delete an AgendaItem which is not referenced,
212         // and that will cascade
213         for (String sAiboId : agendaItemsIds) {
214             boolean bReferenced = agendaItemIdsReferenced.contains(sAiboId);
215             if (bReferenced == false) {
216                 AgendaItemBo aibo = mapIdToBo.get(sAiboId);
217                 getDataObjectService().delete(aibo);
218             }
219         }
220 
221         return to(agendaBoUpdated);
222     }
223 
224     @Override
225     public void deleteAgenda(String agendaId) {
226         if (agendaId == null) {
227             throw new RiceIllegalArgumentException("agendaId is null");
228         }
229 
230         final AgendaBo bo = getDataObjectService().find(AgendaBo.class, agendaId);
231 
232         if (bo == null) {
233             throw new IllegalStateException("the Agenda to delete does not exist: " + agendaId);
234         }
235 
236         // delete orphan agenda items, if needed
237         AgendaItemBo firstAgendaItem = bo.getFirstItem();
238 
239         if (firstAgendaItem != null) {
240             getDataObjectService().delete(firstAgendaItem);
241             getDataObjectService().flush(AgendaItemBo.class);
242 
243             bo.setFirstItem(null);
244             bo.setItems(null);
245         }
246 
247         getDataObjectService().delete(bo);
248     }
249 
250     /**
251      * This overridden method retrieves an Agenda from the repository
252      */
253     @Override
254     public AgendaDefinition getAgendaByAgendaId(String agendaId) {
255         if (StringUtils.isBlank(agendaId)) {
256             throw new RiceIllegalArgumentException("agenda id is null or blank");
257         }
258         AgendaBo bo = getDataObjectService().find(AgendaBo.class, agendaId);
259         return to(bo);
260     }
261 
262     /**
263      * This overridden method retrieves an agenda from the repository
264      */
265     @Override
266     public AgendaDefinition getAgendaByNameAndContextId(String name, String contextId) {
267         if (StringUtils.isBlank(name)) {
268             throw new RiceIllegalArgumentException("name is blank");
269         }
270         if (StringUtils.isBlank(contextId)) {
271             throw new RiceIllegalArgumentException("contextId is blank");
272         }
273 
274         final Map<String, Object> map = new HashMap<String, Object>();
275         map.put("name", name);
276         map.put("contextId", contextId);
277 
278         AgendaBo myAgenda = findSingleMatching(getDataObjectService(), AgendaBo.class, map);
279         return to(myAgenda);
280     }
281 
282     /**
283      * This overridden method retrieves a set of agendas from the repository
284      */
285     @Override
286     public List<AgendaDefinition> getAgendasByContextId(String contextId) {
287         if (StringUtils.isBlank(contextId)) {
288             throw new RiceIllegalArgumentException("context ID is null or blank");
289         }
290         final Map<String, Object> map = new HashMap<String, Object>();
291         map.put("contextId", contextId);
292         List<AgendaBo> bos = findMatching(getDataObjectService(), AgendaBo.class, map);
293 
294         return convertAgendaBosToImmutables(bos);
295     }
296 
297     /**
298      * This overridden method creates a new Agenda in the repository
299      */
300     @Override
301     public AgendaItemDefinition createAgendaItem(AgendaItemDefinition agendaItem) {
302         if (agendaItem == null) {
303             throw new RiceIllegalArgumentException("agendaItem is null");
304         }
305         if (agendaItem.getId() != null) {
306             final AgendaDefinition existing = getAgendaByAgendaId(agendaItem.getId());
307             if (existing != null) {
308                 throw new IllegalStateException("the agendaItem to create already exists: " + agendaItem);
309             }
310         }
311 
312         AgendaItemBo bo = AgendaItemBo.from(agendaItem);
313         if (StringUtils.isNotBlank(agendaItem.getRuleId()) && agendaItem.getRule() == null ) {
314             bo.setRule(getDataObjectService().find(RuleBo.class, agendaItem.getRuleId()));
315         }
316 
317         if (StringUtils.isNotBlank(agendaItem.getAlwaysId()) && agendaItem.getAlways() == null ) {
318             bo.setAlways(getDataObjectService().find(AgendaItemBo.class, agendaItem.getAlwaysId()));
319         }
320 
321         if (StringUtils.isNotBlank(agendaItem.getWhenTrueId()) && agendaItem.getWhenTrue() == null ) {
322             bo.setWhenTrue(getDataObjectService().find(AgendaItemBo.class, agendaItem.getWhenTrueId()));
323         }
324 
325         if (StringUtils.isNotBlank(agendaItem.getWhenFalseId()) && agendaItem.getWhenFalse() == null ) {
326             bo.setWhenFalse(getDataObjectService().find(AgendaItemBo.class, agendaItem.getWhenFalseId()));
327         }
328 
329         bo = getDataObjectService().save(bo, PersistenceOption.FLUSH);
330         return AgendaItemBo.to(bo);
331     }
332 
333     /**
334      * This overridden method updates an existing Agenda in the repository
335      */
336     @Override
337     public AgendaItemDefinition updateAgendaItem(AgendaItemDefinition agendaItem) {
338         if (agendaItem == null) {
339             throw new RiceIllegalArgumentException("agendaItem is null");
340         }
341         final String agendaItemIdKey = agendaItem.getId();
342         final AgendaItemDefinition existing = getAgendaItemById(agendaItemIdKey);
343         if (existing == null) {
344             throw new IllegalStateException("the agenda item does not exist: " + agendaItem);
345         }
346         final AgendaItemDefinition toUpdate;
347         if (existing.getId().equals(agendaItem.getId())) {
348             toUpdate = agendaItem;
349         } else {
350             final AgendaItemDefinition.Builder builder = AgendaItemDefinition.Builder.create(agendaItem);
351             builder.setId(existing.getId());
352             toUpdate = builder.build();
353         }
354 
355         AgendaItemBo aiBo = AgendaItemBo.from(toUpdate);
356         final AgendaItemBo updatedData = getDataObjectService().save(aiBo, PersistenceOption.FLUSH);
357         return AgendaItemBo.to(updatedData);
358     }
359 
360     /**
361      * This overridden method adds a new AgendaItemDefinition to the repository
362      */
363     @Override
364     public void addAgendaItem(AgendaItemDefinition agendaItem, String parentId, Boolean position) {
365         if (agendaItem == null) {
366             throw new RiceIllegalArgumentException("agendaItem is null");
367         }
368         AgendaItemDefinition parent = null;
369         if (parentId != null) {
370             parent = getAgendaItemById(parentId);
371             if (parent == null) {
372                 throw new IllegalStateException(
373                         "parent agendaItem does not exist in repository. parentId = " + parentId);
374             }
375         }
376         // create new AgendaItemDefinition
377         final AgendaItemDefinition toCreate;
378         if (agendaItem.getId() == null) {
379             final AgendaItemDefinition.Builder builder = AgendaItemDefinition.Builder.create(agendaItem);
380             builder.setId(AgendaItemBo.agendaItemIdIncrementer.getNewId());
381             toCreate = builder.build();
382         } else {
383             toCreate = agendaItem;
384         }
385         createAgendaItem(toCreate);
386 
387         // link it to it's parent (for whenTrue/whenFalse, sibling for always
388         if (parentId != null) {
389             final AgendaItemDefinition.Builder builder = AgendaItemDefinition.Builder.create(parent);
390             if (position == null) {
391                 builder.setAlwaysId(toCreate.getId());
392             } else if (position.booleanValue()) {
393                 builder.setWhenTrueId(toCreate.getId());
394             } else if (!position.booleanValue()) {
395                 builder.setWhenFalseId(toCreate.getId());
396             }
397             final AgendaItemDefinition parentToUpdate = builder.build();
398             updateAgendaItem(parentToUpdate);
399         }
400     }
401 
402     /**
403      * This overridden method retrieves an AgendaItemDefinition from the repository
404      */
405     @Override
406     public AgendaItemDefinition getAgendaItemById(String id) {
407         if (StringUtils.isBlank(id)) {
408             throw new RiceIllegalArgumentException("agenda item id is null or blank");
409         }
410 
411         AgendaItemBo bo = getDataObjectService().find(AgendaItemBo.class, id);
412 
413         return AgendaItemBo.to(bo);
414     }
415 
416     @Override
417     public List<AgendaItemDefinition> getAgendaItemsByAgendaId(String agendaId) {
418         if (StringUtils.isBlank(agendaId)) {
419             throw new RiceIllegalArgumentException("agenda id is null or null");
420         }
421         List<AgendaItemDefinition> results = null;
422 
423         Collection<AgendaItemBo> bos = findMatching(getDataObjectService(), AgendaItemBo.class, Collections.singletonMap(
424                 "agendaId", agendaId));
425 
426         if (CollectionUtils.isEmpty(bos)) {
427             results = Collections.emptyList();
428         } else {
429             results = Collections.unmodifiableList(ModelObjectUtils.transform(bos, toAgendaItemDefinition));
430         }
431 
432         return results;
433     }
434 
435     @Override
436     public List<AgendaDefinition> getAgendasByType(String typeId) throws RiceIllegalArgumentException {
437         if (StringUtils.isBlank(typeId)) {
438             throw new RiceIllegalArgumentException("type ID is null or blank");
439         }
440 
441         final Map<String, Object> map = new HashMap<String, Object>();
442         map.put("typeId", typeId);
443         List<AgendaBo> bos = findMatching(getDataObjectService(), AgendaBo.class, map);
444 
445         return convertAgendaBosToImmutables(bos);
446     }
447 
448     @Override
449     public List<AgendaDefinition> getAgendasByTypeAndContext(String typeId, String contextId)
450             throws RiceIllegalArgumentException {
451         if (StringUtils.isBlank(typeId)) {
452             throw new RiceIllegalArgumentException("type ID is null or blank");
453         }
454         if (StringUtils.isBlank(contextId)) {
455             throw new RiceIllegalArgumentException("context ID is null or blank");
456         }
457         final Map<String, Object> map = new HashMap<String, Object>();
458         map.put("typeId", typeId);
459         map.put("contextId", contextId);
460         Collection<AgendaBo> bos = findMatching(getDataObjectService(), AgendaBo.class, map);
461 
462         return convertAgendaBosToImmutables(bos);
463     }
464 
465     @Override
466     public List<AgendaItemDefinition> getAgendaItemsByType(String typeId) throws RiceIllegalArgumentException {
467         return findAgendaItemsForAgendas(getAgendasByType(typeId));
468     }
469 
470     @Override
471     public List<AgendaItemDefinition> getAgendaItemsByContext(String contextId) throws RiceIllegalArgumentException {
472         return findAgendaItemsForAgendas(getAgendasByContextId(contextId));
473     }
474 
475     @Override
476     public List<AgendaItemDefinition> getAgendaItemsByTypeAndContext(String typeId, String contextId)
477             throws RiceIllegalArgumentException {
478         return findAgendaItemsForAgendas(getAgendasByTypeAndContext(typeId, contextId));
479     }
480 
481     @Override
482     public void deleteAgendaItem(String agendaItemId) throws RiceIllegalArgumentException {
483         if (StringUtils.isBlank(agendaItemId)) {
484             throw new RiceIllegalArgumentException("agendaItemId must not be blank or null");
485         }
486 
487         deleteMatching(getDataObjectService(), AgendaItemBo.class, Collections.singletonMap("id", agendaItemId));
488     }
489 
490     private List<AgendaItemDefinition> findAgendaItemsForAgendas(List<AgendaDefinition> agendaDefinitions) {
491         List<AgendaItemDefinition> results = null;
492 
493         if (!CollectionUtils.isEmpty(agendaDefinitions)) {
494             List<AgendaItemBo> boResults = new ArrayList<AgendaItemBo>(agendaDefinitions.size());
495 
496             List<String> agendaIds = new ArrayList<String>(20);
497             for (AgendaDefinition agendaDefinition : agendaDefinitions) {
498                 agendaIds.add(agendaDefinition.getId());
499 
500                 if (agendaIds.size() == 20) {
501                     // fetch batch
502 
503                     Predicate predicate = in("agendaId", agendaIds.toArray());
504                     QueryByCriteria criteria = QueryByCriteria.Builder.fromPredicates(predicate);
505                     QueryResults<AgendaItemBo> batch = getDataObjectService().findMatching(AgendaItemBo.class,
506                             criteria);
507 
508                     boResults.addAll(batch.getResults());
509 
510                     // reset agendaIds
511                     agendaIds.clear();
512                 }
513             }
514 
515             if (agendaIds.size() > 0) {
516                 Predicate predicate = in("agendaId", agendaIds.toArray());
517                 QueryByCriteria criteria = QueryByCriteria.Builder.fromPredicates(predicate);
518                 QueryResults<AgendaItemBo> batch = getDataObjectService().findMatching(AgendaItemBo.class, criteria);
519 
520                 boResults.addAll(batch.getResults());
521             }
522 
523             results = Collections.unmodifiableList(ModelObjectUtils.transform(boResults, toAgendaItemDefinition));
524         } else {
525             results = Collections.emptyList();
526         }
527 
528         return results;
529     }
530 
531     /**
532      * Converts a Set<AgendaBo> to an Unmodifiable Set<Agenda>
533      *
534      * @param agendaBos a mutable Set<AgendaBo> to made completely immutable.
535      * @return An unmodifiable Set<Agenda>
536      */
537     public List<AgendaDefinition> convertAgendaBosToImmutables(final Collection<AgendaBo> agendaBos) {
538         if (CollectionUtils.isEmpty(agendaBos)) {
539             return Collections.emptyList();
540         }
541         return Collections.unmodifiableList(ModelObjectUtils.transform(agendaBos, toAgendaDefinition));
542     }
543 
544     /**
545      * Converts a mutable bo to it's immutable counterpart
546      *
547      * @param bo the mutable business object
548      * @return the immutable object
549      */
550     @Override
551     public AgendaDefinition to(AgendaBo bo) {
552         if (bo == null) {
553             return null;
554         }
555         return org.kuali.rice.krms.api.repository.agenda.AgendaDefinition.Builder.create(bo).build();
556     }
557 
558     /**
559      * Converts a immutable object to it's mutable bo counterpart
560      *
561      * @param im immutable object
562      * @return the mutable bo
563      */
564     @Override
565     public AgendaBo from(AgendaDefinition im) {
566         if (im == null) {
567             return null;
568         }
569 
570         AgendaBo bo = new AgendaBo();
571         bo.setId(im.getId());
572         bo.setName(im.getName());
573         bo.setTypeId(im.getTypeId());
574         bo.setContextId(im.getContextId());
575         bo.setFirstItemId(im.getFirstItemId());
576         bo.setVersionNumber(im.getVersionNumber());
577         bo.setActive(im.isActive());
578         Set<AgendaAttributeBo> attributes = buildAgendaAttributeBo(im, bo);
579 
580         bo.setAttributeBos(attributes);
581 
582         return bo;
583     }
584 
585     private Set<AgendaAttributeBo> buildAgendaAttributeBo(AgendaDefinition im, AgendaBo agendaBo) {
586         Set<AgendaAttributeBo> attributes = new HashSet<AgendaAttributeBo>();
587 
588         // build a map from attribute name to definition
589         Map<String, KrmsAttributeDefinition> attributeDefinitionMap = new HashMap<String, KrmsAttributeDefinition>();
590 
591         List<KrmsAttributeDefinition> attributeDefinitions =
592                 getAttributeDefinitionService().findAttributeDefinitionsByType(im.getTypeId());
593 
594         for (KrmsAttributeDefinition attributeDefinition : attributeDefinitions) {
595             attributeDefinitionMap.put(attributeDefinition.getName(), attributeDefinition);
596         }
597 
598         // for each entry, build an AgendaAttributeBo and add it to the set
599         for (Entry<String, String> entry : im.getAttributes().entrySet()) {
600             KrmsAttributeDefinition attrDef = attributeDefinitionMap.get(entry.getKey());
601 
602             if (attrDef != null) {
603                 AgendaAttributeBo attributeBo = new AgendaAttributeBo();
604                 attributeBo.setAgenda(agendaBo);
605                 attributeBo.setValue(entry.getValue());
606                 attributeBo.setAttributeDefinition(KrmsAttributeDefinitionBo.from(attrDef));
607                 attributes.add(attributeBo);
608             } else {
609                 throw new RiceIllegalStateException("there is no attribute definition with the name '" +
610                         entry.getKey() + "' that is valid for the agenda type with id = '" + im.getTypeId() + "'");
611             }
612         }
613         return attributes;
614     }
615 
616     /**
617      * Gets the {@link DataObjectService}.
618      *
619      * @return the {@link DataObjectService}
620      */
621     public DataObjectService getDataObjectService() {
622         if (dataObjectService == null) {
623             dataObjectService = KRADServiceLocator.getDataObjectService();
624         }
625 
626         return dataObjectService;
627     }
628 
629     /**
630      * Sets the {@link DataObjectService}.
631      *
632      * @param dataObjectService the {@link DataObjectService} to set
633      */
634     public void setDataObjectService(final DataObjectService dataObjectService) {
635         this.dataObjectService = dataObjectService;
636     }
637 
638     /**
639      * Gets the {@link KrmsAttributeDefinitionService}.
640      *
641      * @return the {@link KrmsAttributeDefinitionService}
642      */
643     public KrmsAttributeDefinitionService getAttributeDefinitionService() {
644         if (attributeDefinitionService == null) {
645             attributeDefinitionService = KrmsRepositoryServiceLocator.getKrmsAttributeDefinitionService();
646         }
647 
648         return attributeDefinitionService;
649     }
650 
651     /**
652      * Sets the {@link KrmsAttributeDefinitionService}.
653      *
654      * @param attributeDefinitionService the {@link KrmsAttributeDefinitionService} to set
655      */
656     public void setAttributeDefinitionService(KrmsAttributeDefinitionService attributeDefinitionService) {
657         this.attributeDefinitionService = attributeDefinitionService;
658     }
659 
660 }