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.krms.impl.repository; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.criteria.QueryByCriteria; 020import org.kuali.rice.core.api.criteria.QueryResults; 021import org.kuali.rice.core.api.exception.RiceIllegalArgumentException; 022import org.kuali.rice.krad.data.DataObjectService; 023import org.kuali.rice.krad.data.PersistenceOption; 024import org.kuali.rice.krad.service.KRADServiceLocator; 025import org.kuali.rice.krms.api.repository.term.TermDefinition; 026import org.kuali.rice.krms.api.repository.term.TermResolverDefinition; 027import org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition; 028import org.kuali.rice.krms.impl.util.KrmsImplConstants; 029import org.springframework.util.CollectionUtils; 030 031import java.util.ArrayList; 032import java.util.Collections; 033import java.util.HashMap; 034import java.util.List; 035import java.util.Map; 036 037import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.findMatching; 038import static org.kuali.rice.krms.impl.repository.BusinessObjectServiceMigrationUtils.findSingleMatching; 039 040/** 041 * Implementation of {@link TermBoService} 042 * 043 * @author Kuali Rice Team (rice.collab@kuali.org) 044 */ 045public class TermBoServiceImpl implements TermBoService { 046 047 private DataObjectService dataObjectService; 048 049 /** 050 * @see org.kuali.rice.krms.impl.repository.TermBoService#getTermSpecificationById(java.lang.String) 051 */ 052 @Override 053 public TermSpecificationDefinition getTermSpecificationById(String id) { 054 TermSpecificationDefinition result = null; 055 056 if (StringUtils.isBlank(id)) { 057 throw new RiceIllegalArgumentException("id must not be blank or null"); 058 } 059 060 TermSpecificationBo termSpecificationBo = getDataObjectService().find(TermSpecificationBo.class, id); 061 062 if (termSpecificationBo != null) { 063 List<ContextValidTermBo> contextValidTermBos = 064 findMatching(getDataObjectService(), ContextValidTermBo.class, 065 Collections.singletonMap("termSpecification.id", termSpecificationBo.getId())); 066 067 if (contextValidTermBos != null) for (ContextValidTermBo contextValidTerm : contextValidTermBos) { 068 termSpecificationBo.getContextIds().add(contextValidTerm.getContextId()); 069 } 070 071 result = TermSpecificationDefinition.Builder.create(termSpecificationBo).build(); 072 } 073 074 return result; 075 } 076 077 /** 078 * @see org.kuali.rice.krms.impl.repository.TermBoService#createTermSpecification(org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition) 079 */ 080 @Override 081 public TermSpecificationDefinition createTermSpecification(TermSpecificationDefinition termSpec) { 082 if (!StringUtils.isBlank(termSpec.getId())) { 083 throw new RiceIllegalArgumentException("for creation, TermSpecification.id must be null"); 084 } 085 086 TermSpecificationBo termSpecBo = TermSpecificationBo.from(termSpec); 087 088 // save relations to the contexts on the BO 089 if (!CollectionUtils.isEmpty(termSpec.getContextIds())) { 090 for (String contextId : termSpec.getContextIds()) { 091 ContextValidTermBo contextValidTerm = new ContextValidTermBo(); 092 contextValidTerm.setContextId(contextId); 093 contextValidTerm.setTermSpecification(termSpecBo); 094 095 termSpecBo.getContextValidTerms().add(contextValidTerm); 096 } 097 } 098 099 termSpecBo = getDataObjectService().save(termSpecBo, PersistenceOption.FLUSH); 100 101 return TermSpecificationBo.to(termSpecBo); 102 } 103 104 @Override 105 public void updateTermSpecification(TermSpecificationDefinition termSpec) throws RiceIllegalArgumentException { 106 if (termSpec == null) { 107 throw new IllegalArgumentException("term specification is null"); 108 } 109 110 // must already exist to be able to update 111 final String termSpecificationId = termSpec.getId(); 112 final TermSpecificationBo existing = getDataObjectService().find(TermSpecificationBo.class, termSpecificationId); 113 114 if (existing == null) { 115 throw new IllegalStateException("the term specification does not exist: " + termSpec); 116 } 117 118 final TermSpecificationDefinition toUpdate; 119 120 if (!existing.getId().equals(termSpec.getId())) { 121 // if passed in id does not match existing id, correct it 122 final TermSpecificationDefinition.Builder builder = TermSpecificationDefinition.Builder.create(termSpec); 123 builder.setId(existing.getId()); 124 toUpdate = builder.build(); 125 } else { 126 toUpdate = termSpec; 127 } 128 129 // copy all updateable fields to bo 130 TermSpecificationBo boToUpdate = TermSpecificationBo.from(toUpdate); 131 reconcileContextValidTerms(existing, boToUpdate); 132 133 // update the rule and create new attributes 134 getDataObjectService().save(boToUpdate, PersistenceOption.FLUSH); 135 } 136 137 /** 138 * Transfer any ContextValidTermBos that still apply from existing to boToUpdate, and create new ContextValidTermBos 139 * for any new context IDs that are found in boToUpdate. 140 * 141 * <p>This method is side effecting, it makes modifications to boToUpdate.contextValidTerms. </p> 142 * 143 * @param existing the TermSpecificationBo which has been fetched from the database 144 * @param boToUpdate the new TermSpecificationBo which will (later) be persisted 145 */ 146 private void reconcileContextValidTerms(TermSpecificationBo existing, 147 TermSpecificationBo boToUpdate) { 148 149 // add all contextValidTerms that still apply 150 for (ContextValidTermBo contextValidTerm : existing.getContextValidTerms()) { 151 if (boToUpdate.getContextIds().contains(contextValidTerm.getContextId())) { 152 boToUpdate.getContextValidTerms().add(contextValidTerm); 153 } 154 } 155 156 // add new contextValidTerms for new context IDs 157 for (String contextId : boToUpdate.getContextIds()) { 158 boolean alreadyInContextValidTerms = false; 159 160 for (ContextValidTermBo contextValidTerm : boToUpdate.getContextValidTerms()) { 161 if (contextId.equals(contextValidTerm.getContextId())) { 162 alreadyInContextValidTerms = true; 163 break; 164 } 165 } 166 167 if (!alreadyInContextValidTerms) { 168 ContextValidTermBo contextValidTerm = new ContextValidTermBo(); 169 contextValidTerm.setContextId(contextId); 170 contextValidTerm.setTermSpecification(boToUpdate); 171 172 boToUpdate.getContextValidTerms().add(contextValidTerm); 173 } 174 } 175 } 176 177 @Override 178 public void deleteTermSpecification(String id) throws RiceIllegalArgumentException { 179 if (id == null) { 180 throw new RiceIllegalArgumentException("agendaId is null"); 181 } 182 183 final TermSpecificationBo existing = getDataObjectService().find(TermSpecificationBo.class, id); 184 185 if (existing == null) { 186 throw new IllegalStateException("the TermSpecification to delete does not exists: " + id); 187 } 188 189 getDataObjectService().delete(existing); 190 } 191 192 /** 193 * @see org.kuali.rice.krms.impl.repository.TermBoService#createTerm(org.kuali.rice.krms.api.repository.term.TermDefinition) 194 */ 195 @Override 196 public TermDefinition createTerm(TermDefinition termDef) { 197 if (!StringUtils.isBlank(termDef.getId())) { 198 throw new RiceIllegalArgumentException("for creation, TermDefinition.id must be null"); 199 } 200 201 TermBo termBo = TermBo.from(termDef); 202 termBo = getDataObjectService().save(termBo, PersistenceOption.FLUSH); 203 204 return TermBo.to(termBo); 205 } 206 207 @Override 208 public void updateTerm(TermDefinition term) throws RiceIllegalArgumentException { 209 if (term == null) { 210 throw new IllegalArgumentException("term is null"); 211 } 212 213 // must already exist to be able to update 214 final String termId = term.getId(); 215 final TermBo existing = getDataObjectService().find(TermBo.class, termId); 216 217 if (existing == null) { 218 throw new IllegalStateException("the term resolver does not exist: " + term); 219 } 220 221 final TermDefinition toUpdate; 222 223 if (!existing.getId().equals(term.getId())) { 224 // if passed in id does not match existing id, correct it 225 final TermDefinition.Builder builder = TermDefinition.Builder.create(term); 226 builder.setId(existing.getId()); 227 toUpdate = builder.build(); 228 } else { 229 toUpdate = term; 230 } 231 232 // copy all updateable fields to bo 233 TermBo boToUpdate = TermBo.from(toUpdate); 234 235 // update the rule and create new attributes 236 getDataObjectService().save(boToUpdate, PersistenceOption.FLUSH); 237 } 238 239 @Override 240 public void deleteTerm(String id) throws RiceIllegalArgumentException { 241 if (id == null) { 242 throw new RiceIllegalArgumentException("termId is null"); 243 } 244 245 TermBo existing = getDataObjectService().find(TermBo.class, id); 246 247 if (existing == null) { 248 throw new IllegalStateException("the term to delete does not exists: " + id); 249 } 250 251 getDataObjectService().delete(existing); 252 } 253 254 /** 255 * @see org.kuali.rice.krms.impl.repository.TermBoService#createTermResolver(org.kuali.rice.krms.api.repository.term.TermResolverDefinition) 256 */ 257 @Override 258 public TermResolverDefinition createTermResolver(TermResolverDefinition termResolver) { 259 if (!StringUtils.isBlank(termResolver.getId())) { 260 throw new RiceIllegalArgumentException("for creation, TermResolverDefinition.id must be null"); 261 } 262 263 TermResolverBo termResolverBo = TermResolverBo.from(termResolver); 264 265 termResolverBo = (TermResolverBo) getDataObjectService().save(termResolverBo, PersistenceOption.FLUSH); 266 267 return TermResolverBo.to(termResolverBo); 268 } 269 270 @Override 271 public void updateTermResolver(TermResolverDefinition termResolver) throws RiceIllegalArgumentException { 272 if (termResolver == null) { 273 throw new IllegalArgumentException("term resolver is null"); 274 } 275 276 // must already exist to be able to update 277 final String termResolverId = termResolver.getId(); 278 final TermResolverBo existing = getDataObjectService().find(TermResolverBo.class, termResolverId); 279 280 if (existing == null) { 281 throw new IllegalStateException("the term resolver does not exist: " + termResolver); 282 } 283 284 final TermResolverDefinition toUpdate; 285 286 if (!existing.getId().equals(termResolver.getId())) { 287 // if passed in id does not match existing id, correct it 288 final TermResolverDefinition.Builder builder = TermResolverDefinition.Builder.create(termResolver); 289 builder.setId(existing.getId()); 290 toUpdate = builder.build(); 291 } else { 292 toUpdate = termResolver; 293 } 294 295 // copy all updateable fields to bo 296 TermResolverBo boToUpdate = TermResolverBo.from(toUpdate); 297 298 // delete any old, existing attributes 299 QueryByCriteria crit = 300 QueryByCriteria.Builder.forAttribute(KrmsImplConstants.PropertyNames.TermResolver.TERM_RESOLVER_ID, toUpdate.getId()).build(); 301 302 getDataObjectService().deleteMatching(TermResolverAttributeBo.class, crit); 303 304 // update the rule and create new attributes 305 getDataObjectService().save(boToUpdate, PersistenceOption.FLUSH); 306 } 307 308 @Override 309 public void deleteTermResolver(String id) throws RiceIllegalArgumentException { 310 if (id == null) { 311 throw new RiceIllegalArgumentException("agendaId is null"); 312 } 313 314 TermSpecificationBo existing = getDataObjectService().find(TermSpecificationBo.class, id); 315 316 if (existing == null) { 317 throw new IllegalStateException("the TermResolver to delete does not exists: " + id); 318 } 319 320 getDataObjectService().delete(existing); 321 } 322 323 /** 324 * @see org.kuali.rice.krms.impl.repository.TermBoService#getTerm(java.lang.String) 325 */ 326 @Override 327 public TermDefinition getTerm(String id) { 328 TermDefinition result = null; 329 330 if (StringUtils.isBlank(id)) { 331 throw new RiceIllegalArgumentException("id must not be blank or null"); 332 } 333 334 TermBo termBo = getDataObjectService().find(TermBo.class, id); 335 336 if (termBo != null) { 337 result = TermBo.to(termBo); 338 } 339 340 return result; 341 } 342 343 /** 344 * @see org.kuali.rice.krms.impl.repository.TermBoService#getTermResolverById(java.lang.String) 345 */ 346 @Override 347 public TermResolverDefinition getTermResolverById(String id) { 348 TermResolverDefinition result = null; 349 350 if (StringUtils.isBlank(id)) { 351 throw new RiceIllegalArgumentException("id must not be blank or null"); 352 } 353 354 TermResolverBo termResolverBo = getDataObjectService().find(TermResolverBo.class, id); 355 356 if (termResolverBo != null) { 357 result = TermResolverBo.to(termResolverBo); 358 } 359 360 return result; 361 } 362 363 @Override 364 public List<TermResolverDefinition> findTermResolversByOutputId(String id, String namespace) { 365 List<TermResolverDefinition> results = null; 366 367 if (StringUtils.isBlank(id)) { 368 throw new RiceIllegalArgumentException("id must not be blank or null"); 369 } 370 371 if (StringUtils.isBlank(namespace)) { 372 throw new RiceIllegalArgumentException("namespace must not be blank or null"); 373 } 374 375 Map<String, String> critMap = new HashMap<String, String>(2); 376 377 critMap.put("outputId", id); 378 critMap.put("namespace", namespace); 379 380 QueryByCriteria crit = QueryByCriteria.Builder.andAttributes(critMap).build(); 381 382 QueryResults<TermResolverBo> termResolverBos = getDataObjectService().findMatching(TermResolverBo.class, crit); 383 384 if (!CollectionUtils.isEmpty(termResolverBos.getResults())) { 385 results = new ArrayList<TermResolverDefinition>(termResolverBos.getResults().size()); 386 387 for (TermResolverBo termResolverBo : termResolverBos.getResults()) { 388 results.add(TermResolverBo.to(termResolverBo)); 389 } 390 } else { 391 results = Collections.emptyList(); 392 } 393 394 return results; 395 } 396 397 @Override 398 public List<TermResolverDefinition> findTermResolversByNamespace(String namespace) { 399 List<TermResolverDefinition> results = null; 400 401 if (StringUtils.isBlank(namespace)) { 402 throw new RiceIllegalArgumentException("namespace must not be blank or null"); 403 } 404 405 QueryByCriteria crit = QueryByCriteria.Builder.forAttribute("namespace", namespace).build(); 406 407 QueryResults<TermResolverBo> termResolverBos = getDataObjectService().findMatching(TermResolverBo.class, crit); 408 409 if (!CollectionUtils.isEmpty(termResolverBos.getResults())) { 410 results = new ArrayList<TermResolverDefinition>(termResolverBos.getResults().size()); 411 412 for (TermResolverBo termResolverBo : termResolverBos.getResults()) { 413 if (termResolverBo != null) { 414 results.add(TermResolverBo.to(termResolverBo)); 415 } 416 } 417 } else { 418 results = Collections.emptyList(); 419 } 420 421 return results; 422 } 423 424 @Override 425 public TermResolverDefinition getTermResolverByNameAndNamespace(String name, 426 String namespace) throws RiceIllegalArgumentException { 427 if (StringUtils.isBlank(name)) { 428 throw new IllegalArgumentException("name is null or blank"); 429 } 430 431 if (StringUtils.isBlank(namespace)) { 432 throw new IllegalArgumentException("namespace is null or blank"); 433 } 434 435 final Map<String, Object> map = new HashMap<String, Object>(); 436 map.put("name", name); 437 map.put("namespace", namespace); 438 TermResolverBo bo = getDataObjectService().find(TermResolverBo.class, map); 439 440 return TermResolverBo.to(bo); 441 } 442 443 @Override 444 public TermSpecificationDefinition getTermSpecificationByNameAndNamespace(String name, 445 String namespace) throws RiceIllegalArgumentException { 446 if (StringUtils.isBlank(name)) { 447 throw new IllegalArgumentException("name is null or blank"); 448 } 449 450 if (StringUtils.isBlank(namespace)) { 451 throw new IllegalArgumentException("namespace is null or blank"); 452 } 453 454 final Map<String, Object> map = new HashMap<String, Object>(); 455 map.put("name", name); 456 map.put("namespace", namespace); 457 TermSpecificationBo bo = findSingleMatching(getDataObjectService(), TermSpecificationBo.class, map); 458 459 return TermSpecificationBo.to(bo); 460 } 461 462 @Override 463 public List<TermSpecificationDefinition> findAllTermSpecificationsByContextId(String contextId) { 464 List<TermSpecificationDefinition> results = null; 465 466 if (StringUtils.isBlank(contextId)) { 467 throw new RiceIllegalArgumentException("contextId must not be blank or null"); 468 } 469 470 QueryByCriteria crit = QueryByCriteria.Builder.forAttribute("contextId", contextId).build(); 471 472 QueryResults<ContextValidTermBo> contextValidTerms = 473 getDataObjectService().findMatching(ContextValidTermBo.class, crit); 474 475 if (!CollectionUtils.isEmpty(contextValidTerms.getResults())) { 476 results = new ArrayList<TermSpecificationDefinition>(contextValidTerms.getResults().size()); 477 478 for (ContextValidTermBo validTerm : contextValidTerms.getResults()) { 479 results.add(TermSpecificationBo.to(validTerm.getTermSpecification())); 480 } 481 } else { 482 results = Collections.emptyList(); 483 } 484 485 return results; 486 } 487 488 /** 489 * Gets the {@link DataObjectService}. 490 * 491 * @return the {@link DataObjectService} 492 */ 493 public DataObjectService getDataObjectService() { 494 if (dataObjectService == null) { 495 dataObjectService = KRADServiceLocator.getDataObjectService(); 496 } 497 498 return dataObjectService; 499 } 500 501 /** 502 * Sets the {@link DataObjectService}. 503 * 504 * @param dataObjectService the {@link DataObjectService} to set 505 */ 506 public void setDataObjectService(DataObjectService dataObjectService) { 507 this.dataObjectService = dataObjectService; 508 } 509 510}