001package org.kuali.rice.krms.impl.repository; 002 003import org.apache.commons.lang.StringUtils; 004import org.kuali.rice.core.api.criteria.Predicate; 005import org.kuali.rice.core.api.criteria.QueryByCriteria; 006import org.kuali.rice.core.api.criteria.QueryResults; 007import org.kuali.rice.core.api.exception.RiceIllegalArgumentException; 008import org.kuali.rice.core.api.exception.RiceIllegalStateException; 009import org.kuali.rice.core.api.mo.ModelObjectUtils; 010import org.kuali.rice.krad.data.DataObjectService; 011import org.kuali.rice.krad.data.PersistenceOption; 012import org.kuali.rice.krad.service.KRADServiceLocator; 013import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition; 014import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition; 015import org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition; 016import org.springframework.util.CollectionUtils; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026 027import static org.kuali.rice.core.api.criteria.PredicateFactory.in; 028import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.deleteMatching; 029import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.findMatching; 030import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.findSingleMatching; 031 032/** 033 * Created by SW Genis on 2014/05/06. 034 */ 035public class KSAgendaBoServiceImpl implements AgendaBoService { 036 037 // TODO: deal with active flag 038 039 private DataObjectService dataObjectService; 040 private KrmsAttributeDefinitionService attributeDefinitionService; 041 042 // used for converting lists of BOs to model objects 043 private static final ModelObjectUtils.Transformer<AgendaItemBo, AgendaItemDefinition> toAgendaItemDefinition = 044 new ModelObjectUtils.Transformer<AgendaItemBo, AgendaItemDefinition>() { 045 public AgendaItemDefinition transform(AgendaItemBo input) { 046 return AgendaItemBo.to(input); 047 }; 048 }; 049 050 // used for converting lists of BOs to model objects 051 private static final ModelObjectUtils.Transformer<AgendaBo, AgendaDefinition> toAgendaDefinition = 052 new ModelObjectUtils.Transformer<AgendaBo, AgendaDefinition>() { 053 public AgendaDefinition transform(AgendaBo input) { 054 return AgendaBo.to(input); 055 }; 056 }; 057 058 059 /** 060 * This overridden method creates a KRMS Agenda in the repository 061 */ 062 @Override 063 public AgendaDefinition createAgenda(AgendaDefinition agenda) { 064 if (agenda == null){ 065 throw new RiceIllegalArgumentException("agenda is null"); 066 } 067 final String nameKey = agenda.getName(); 068 final String contextId = agenda.getContextId(); 069 final AgendaDefinition existing = getAgendaByNameAndContextId(nameKey, contextId); 070 if (existing != null){ 071 throw new IllegalStateException("the agenda to create already exists: " + agenda); 072 } 073 074 AgendaBo agendaBo = from(agenda); 075 agendaBo = dataObjectService.save(agendaBo, PersistenceOption.FLUSH); 076 return to(agendaBo); 077 } 078 079 /** 080 * This overridden method updates an existing Agenda in the repository 081 */ 082 @Override 083 public void updateAgenda(AgendaDefinition agenda) { 084 if (agenda == null){ 085 throw new RiceIllegalArgumentException("agenda is null"); 086 } 087 088 // must already exist to be able to update 089 final String agendaIdKey = agenda.getId(); 090 final AgendaBo existing = dataObjectService.find(AgendaBo.class, agendaIdKey); 091 if (existing == null) { 092 throw new IllegalStateException("the agenda does not exist: " + agenda); 093 } 094 final AgendaDefinition toUpdate; 095 if (existing.getId().equals(agenda.getId())) { 096 toUpdate = agenda; 097 } else { 098 // if passed in id does not match existing id, correct it 099 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}