View Javadoc

1   /**
2    * Copyright 2012 The Kuali Foundation Licensed under the Educational Community
3    * License, Version 2.0 (the "License"); you may not use this file except in
4    * compliance with the License. You may obtain a copy of the License at
5    *
6    * http://www.osedu.org/licenses/ECL-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11   * License for the specific language governing permissions and limitations under
12   * the License.
13   *
14   * Created by Charles on 2/28/12
15   */
16  package org.kuali.student.enrollment.class2.courseofferingset.service.impl;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.log4j.Logger;
20  import org.kuali.rice.core.api.criteria.GenericQueryResults;
21  import org.kuali.rice.core.api.criteria.QueryByCriteria;
22  import org.kuali.student.enrollment.class2.courseofferingset.dao.SocDao;
23  import org.kuali.student.enrollment.class2.courseofferingset.dao.SocRolloverResultDao;
24  import org.kuali.student.enrollment.class2.courseofferingset.dao.SocRolloverResultItemDao;
25  import org.kuali.student.enrollment.class2.courseofferingset.model.SocAttributeEntity;
26  import org.kuali.student.enrollment.class2.courseofferingset.model.SocEntity;
27  import org.kuali.student.enrollment.class2.courseofferingset.model.SocRolloverResultAttributeEntity;
28  import org.kuali.student.enrollment.class2.courseofferingset.model.SocRolloverResultEntity;
29  import org.kuali.student.enrollment.class2.courseofferingset.model.SocRolloverResultItemEntity;
30  import org.kuali.student.enrollment.class2.courseofferingset.model.SocRolloverResultOptionEntity;
31  import org.kuali.student.enrollment.courseofferingset.dto.SocInfo;
32  import org.kuali.student.enrollment.courseofferingset.dto.SocRolloverResultInfo;
33  import org.kuali.student.enrollment.courseofferingset.dto.SocRolloverResultItemInfo;
34  import org.kuali.student.enrollment.courseofferingset.service.CourseOfferingSetService;
35  import org.kuali.student.enrollment.courseofferingset.service.CourseOfferingSetServiceBusinessLogic;
36  import org.kuali.student.r2.common.assembler.TransformUtility;
37  import org.kuali.student.r2.common.criteria.CriteriaLookupService;
38  import org.kuali.student.r2.common.dto.AttributeInfo;
39  import org.kuali.student.r2.common.dto.ContextInfo;
40  import org.kuali.student.r2.common.dto.StatusInfo;
41  import org.kuali.student.r2.common.dto.ValidationResultInfo;
42  import org.kuali.student.r2.common.exceptions.DataValidationErrorException;
43  import org.kuali.student.r2.common.exceptions.DependentObjectsExistException;
44  import org.kuali.student.r2.common.exceptions.DoesNotExistException;
45  import org.kuali.student.r2.common.exceptions.InvalidParameterException;
46  import org.kuali.student.r2.common.exceptions.MissingParameterException;
47  import org.kuali.student.r2.common.exceptions.OperationFailedException;
48  import org.kuali.student.r2.common.exceptions.PermissionDeniedException;
49  import org.kuali.student.r2.common.exceptions.ReadOnlyException;
50  import org.kuali.student.r2.common.exceptions.VersionMismatchException;
51  import org.kuali.student.r2.common.util.constants.CourseOfferingSetServiceConstants;
52  import org.kuali.student.r2.common.util.date.DateFormatters;
53  import org.kuali.student.r2.core.class1.state.service.StateService;
54  import org.kuali.student.r2.core.class1.state.service.StateTransitionsHelper;
55  import org.springframework.transaction.annotation.Transactional;
56  
57  import javax.annotation.Resource;
58  import java.util.ArrayList;
59  import java.util.Arrays;
60  import java.util.Date;
61  import java.util.HashSet;
62  import java.util.List;
63  import java.util.Map;
64  import java.util.Set;
65  
66  public class CourseOfferingSetServiceImpl implements CourseOfferingSetService {
67      final static Logger LOG = Logger.getLogger(CourseOfferingSetServiceImpl.class);
68  
69      @Resource
70      private SocDao socDao;
71      @Resource
72      private SocRolloverResultDao socRorDao;
73      @Resource
74      private SocRolloverResultItemDao socRorItemDao;
75      private CourseOfferingSetServiceBusinessLogic businessLogic;
76      private CriteriaLookupService criteriaLookupService;
77      private StateTransitionsHelper stateTransitionsHelper;
78      private StateService stateService;
79  
80      public CourseOfferingSetServiceBusinessLogic getBusinessLogic() {
81          return businessLogic;
82      }
83  
84      public void setBusinessLogic(CourseOfferingSetServiceBusinessLogic businessLogic) {
85          this.businessLogic = businessLogic;
86      }
87  
88      public SocDao getSocDao() {
89          return socDao;
90      }
91  
92      public void setSocDao(SocDao socDao) {
93          this.socDao = socDao;
94      }
95  
96      public SocRolloverResultDao getSocRorDao() {
97          return socRorDao;
98      }
99  
100     public void setSocRorDao(SocRolloverResultDao socRorDao) {
101         this.socRorDao = socRorDao;
102     }
103 
104     public SocRolloverResultItemDao getSocRorItemDao() {
105         return socRorItemDao;
106     }
107 
108     public void setSocRorItemDao(SocRolloverResultItemDao socRorItemDao) {
109         this.socRorItemDao = socRorItemDao;
110     }
111 
112     public StateTransitionsHelper getStateTransitionsHelper() {
113         return stateTransitionsHelper;
114     }
115 
116     public void setStateTransitionsHelper(StateTransitionsHelper stateTransitionsHelper) {
117         this.stateTransitionsHelper = stateTransitionsHelper;
118     }
119 
120     ////
121     //// implement service methods
122     ////
123     @Override
124     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
125     public SocInfo createSoc(String termId, String typeKey, SocInfo info, ContextInfo context) throws DoesNotExistException,
126             DataValidationErrorException, InvalidParameterException, MissingParameterException, OperationFailedException,
127             PermissionDeniedException, ReadOnlyException {
128         if (!termId.equals(info.getTermId())) {
129             throw new InvalidParameterException("termId does not match the value in the info object");
130         }
131         if (!typeKey.equals(info.getTypeKey())) {
132             throw new InvalidParameterException("typeKey does not match the value in the info object");
133         }
134         // verify socInfo has the the inital state of the Soc
135          List<String> initSocStates = stateService.getInitialStatesByLifecycle(CourseOfferingSetServiceConstants.SOC_LIFECYCLE_KEY, context);
136         if(!initSocStates.isEmpty()) {
137             if(!initSocStates.contains(info.getStateKey())) {
138                 throw new InvalidParameterException("Wrong initial SOC state key");
139             }
140         }
141 
142         SocEntity entity = new SocEntity(info);
143         this.logStateChange(entity, entity.getSocState(), context);
144         entity.setEntityCreated(context);
145         socDao.persist(entity);
146         return entity.toDto();
147     }
148 
149     @Override
150     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
151     public SocRolloverResultInfo createSocRolloverResult(String typeKey, SocRolloverResultInfo info, ContextInfo context) throws
152             DoesNotExistException,
153             DataValidationErrorException, InvalidParameterException, MissingParameterException, OperationFailedException,
154             PermissionDeniedException, ReadOnlyException {
155         if (!typeKey.equals(info.getTypeKey())) {
156             throw new InvalidParameterException("TypeKey does not match the value in the info object");
157         }
158         SocRolloverResultEntity entity = new SocRolloverResultEntity(info);
159         entity.setId(info.getId());
160         entity.setSocRorType(typeKey);
161 
162         entity.setEntityCreated(context);
163 
164         socRorDao.persist(entity);
165         return entity.toDto();
166     }
167 
168     @Override
169     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
170     public SocRolloverResultItemInfo createSocRolloverResultItem(String socRorId, String typeKey, SocRolloverResultItemInfo info, ContextInfo context) throws
171             DoesNotExistException,
172             DataValidationErrorException, InvalidParameterException, MissingParameterException, OperationFailedException,
173             PermissionDeniedException, ReadOnlyException {
174         if (!typeKey.equals(info.getTypeKey())) {
175             throw new InvalidParameterException("TypeKey does not match the value in the info object");
176         }
177         SocRolloverResultItemEntity entity = new SocRolloverResultItemEntity(info);
178         entity.setId(info.getId());
179         entity.setSocRorType(typeKey);
180 
181         entity.setEntityCreated(context);
182 
183         socRorItemDao.persist(entity);
184         return entity.toDto();
185     }
186 
187     @Override
188     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
189     public Integer createSocRolloverResultItems(String socRorId, String typeKey, List<SocRolloverResultItemInfo> infos, ContextInfo context)
190             throws DoesNotExistException,
191             DataValidationErrorException, InvalidParameterException, MissingParameterException, OperationFailedException,
192             PermissionDeniedException, ReadOnlyException {
193         int count = 0;
194         for (SocRolloverResultItemInfo info : infos) {
195             count++;
196             if (!typeKey.equals(info.getTypeKey())) {
197                 throw new InvalidParameterException("TypeKey does not match the value in the info object " + count);
198             }
199             if (!socRorId.equals(info.getSocRolloverResultId())) {
200                 throw new InvalidParameterException("rollover result id does not match the value in the info object " + count);
201             }
202             SocRolloverResultItemEntity entity = new SocRolloverResultItemEntity(info);
203             entity.setId(info.getId());
204             entity.setSocRorType(typeKey);
205 
206             entity.setEntityCreated(context);
207 
208             socRorItemDao.persist(entity);
209         }
210         return Integer.valueOf(count);
211     }
212 
213     @Override
214     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
215     public Integer deleteCourseOfferingsBySoc(String socId, ContextInfo context) throws DoesNotExistException,
216             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
217         return this.businessLogic.deleteCourseOfferingsBySoc(socId, context);
218     }
219 
220     @Override
221     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
222     public StatusInfo deleteSoc(String id, ContextInfo context) throws DependentObjectsExistException, DoesNotExistException,
223             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
224         SocEntity entity = socDao.find(id);
225         if (null == entity) {
226             throw new DoesNotExistException(id);
227         }
228         socDao.remove(entity);
229         StatusInfo status = new StatusInfo();
230         status.setSuccess(Boolean.TRUE);
231         return status;
232     }
233 
234     @Override
235     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
236     public StatusInfo deleteSocRolloverResult(String id, ContextInfo context) throws DoesNotExistException,
237             DependentObjectsExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
238             PermissionDeniedException {
239         SocRolloverResultEntity entity = socRorDao.find(id);
240         if (null == entity) {
241             throw new DoesNotExistException(id);
242         }
243         List<SocRolloverResultItemInfo> items = this.getSocRolloverResultItemsByResultId(id, context);
244         if (!items.isEmpty()) {
245             throw new DependentObjectsExistException(items.size() + " items exist");
246         }
247         socRorDao.remove(entity);
248         StatusInfo status = new StatusInfo();
249         status.setSuccess(Boolean.TRUE);
250         return status;
251     }
252 
253     @Override
254     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
255     public StatusInfo deleteSocRolloverResultItem(String id, ContextInfo context) throws
256             DoesNotExistException,
257             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
258         SocRolloverResultItemEntity entity = socRorItemDao.find(id);
259         if (null == entity) {
260             throw new DoesNotExistException(id);
261         }
262         socRorItemDao.remove(entity);
263         StatusInfo status = new StatusInfo();
264         status.setSuccess(Boolean.TRUE);
265         return status;
266     }
267 
268     @Override
269     @Transactional(readOnly = true)
270     public List<String> getCourseOfferingIdsBySoc(String socId, ContextInfo context) throws DoesNotExistException,
271             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
272         return this.businessLogic.getCourseOfferingIdsBySoc(socId, context);
273     }
274 
275     @Override
276     @Transactional(readOnly = true)
277     public List<String> getCourseOfferingIdsWithUnscheduledFinalExamsBySoc(String socId, ContextInfo context) throws
278             DoesNotExistException,
279             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
280         throw new OperationFailedException("Configuration error Implemented in the calculuation layer");
281     }
282 
283     @Override
284     @Transactional(readOnly = true)
285     public List<String> getPublishedCourseOfferingIdsBySoc(String socId, ContextInfo context) throws DoesNotExistException,
286             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
287         return this.businessLogic.getPublishedCourseOfferingIdsBySoc(socId, context);
288     }
289 
290     @Override
291     @Transactional(readOnly = true)
292     public SocInfo getSoc(String id, ContextInfo context) throws DoesNotExistException, InvalidParameterException,
293             MissingParameterException, OperationFailedException, PermissionDeniedException {
294         SocEntity entity = socDao.find(id);
295         if (null == entity) {
296             throw new DoesNotExistException(id);
297         }
298         return entity.toDto();
299     }
300 
301     @Override
302     @Transactional(readOnly = true)
303     public List<String> getSocIdsByCourseOffering(String courseOfferingId, ContextInfo context) throws DoesNotExistException,
304             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
305         throw new OperationFailedException("Configuration error Implemented in the calculuation layer");
306     }
307 
308     @Override
309     @Transactional(readOnly = true)
310     public List<String> getSocIdsByTerm(String termId, ContextInfo context) throws DoesNotExistException,
311             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
312         return socDao.getSocIdsByTerm(termId);
313     }
314 
315     @Override
316     @Transactional(readOnly = true)
317     public List<String> getSocIdsByTermAndSubjectArea(String termId, String subjectArea, ContextInfo context) throws
318             DoesNotExistException,
319             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
320 
321         return socDao.getSocIdsByTermAndSubjectArea(termId, subjectArea);
322     }
323 
324     @Override
325     @Transactional(readOnly = true)
326     public List<String> getSocIdsByTermAndUnitsContentOwner(String termId, String unitsContentOwnerId, ContextInfo context) throws
327             DoesNotExistException,
328             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
329         return socDao.getSocIdsByTermAndUnitsContentOwner(termId, unitsContentOwnerId);
330     }
331 
332     @Override
333     @Transactional(readOnly = true)
334     public List<String> getSocIdsByType(String typeKey, ContextInfo context) throws DoesNotExistException,
335             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
336         return socDao.getSocIdsByType(typeKey);
337     }
338 
339     @Override
340     @Transactional(readOnly = true)
341     public List<SocInfo> searchForSocs(QueryByCriteria criteria,
342                                        ContextInfo context)
343             throws InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
344 
345         GenericQueryResults<SocEntity> results = criteriaLookupService.lookup(SocEntity.class, criteria);
346         List<SocInfo> socInfos = new ArrayList<SocInfo>(results.getResults().size());
347         for (SocEntity socEntity : results.getResults()) {
348             socInfos.add(socEntity.toDto());
349         }
350         return socInfos;
351     }
352 
353     @Override
354     @Transactional(readOnly = true)
355     public List<String> searchForSocIds(QueryByCriteria criteria, ContextInfo context)
356             throws InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
357         GenericQueryResults<String> results = criteriaLookupService.lookupIds(SocEntity.class, criteria);
358         return results.getResults();
359     }
360 
361     @Override
362     @Transactional(readOnly = true)
363     public SocRolloverResultInfo getSocRolloverResult(String id, ContextInfo context) throws DoesNotExistException,
364             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
365         SocRolloverResultEntity entity = socRorDao.find(id);
366         if (null == entity) {
367             throw new DoesNotExistException(id);
368         }
369         SocRolloverResultInfo info = entity.toDto();
370         this.updateCalculatedFields(info, context);
371         return info;
372     }
373 
374     // TODO: implement this logic with direct counts for efficiency once the logic for the counts settles down.
375     // My GUT says that they may want more counts than just the 2 we are getting now... I.e. count of warnings?    
376     private void updateCalculatedFields(SocRolloverResultInfo info, ContextInfo context) throws OperationFailedException {
377         try {
378             if (info.getSourceSocId() != null) {
379                 SocInfo sourceSoc = this.getSoc(info.getSourceSocId(), context);
380                 info.setSourceTermId(sourceSoc.getTermId());
381             }
382             // only do the calc once finished or the querying while running will be too long
383             if (info.getStateKey().equals(CourseOfferingSetServiceConstants.FINISHED_RESULT_STATE_KEY)) {
384                 List<SocRolloverResultItemInfo> items = this.getSocRolloverResultItemsByResultId(info.getId(), context);
385                 int success = 0;
386                 int failure = 0;
387                 for (SocRolloverResultItemInfo item : items) {
388                     if (CourseOfferingSetServiceConstants.SUCCESSFUL_RESULT_ITEM_STATES.contains(item.getStateKey())) {
389                         success++;
390                     } else {
391                         failure++;
392                     }
393                 }
394                 info.setCourseOfferingsCreated(success);
395                 info.setCourseOfferingsSkipped(failure);
396             }
397         } catch (Exception ex) {
398             throw new OperationFailedException("unexpected", ex);
399         }
400     }
401 
402     @Override
403     public List<SocRolloverResultInfo> getSocRolloverResultsBySourceAndTargetSocs(String sourceSocId, String targetSocId, ContextInfo context) throws
404             DoesNotExistException,
405             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
406         // TODO: implement this as a JPQL search
407         List<SocRolloverResultInfo> list = new ArrayList<SocRolloverResultInfo>();
408         List<String> ids = this.getSocRolloverResultIdsBySourceSoc(sourceSocId, context);
409         for (String id : ids) {
410             SocRolloverResultInfo info = this.getSocRolloverResult(id, context);
411             if (targetSocId.equals(info.getTargetSocId())) {
412                 list.add(info);
413             }
414         }
415         return list;
416     }
417 
418     @Override
419     @Transactional(readOnly = true)
420     public List<String> getSocRolloverResultIdsBySourceSoc(String sourceSocId, ContextInfo context) throws DoesNotExistException,
421             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
422         return socRorDao.getSocRolloverResultIdsBySourceSocId(sourceSocId);
423     }
424 
425     @Override
426     @Transactional(readOnly = true)
427     public List<String> getSocRolloverResultIdsByTargetSoc(String targetSocId, ContextInfo context) throws DoesNotExistException,
428             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
429         return socRorDao.getSocRolloverResultIdsByTargetSocId(targetSocId);
430     }
431 
432     @Override
433     @Transactional(readOnly = true)
434     public SocRolloverResultItemInfo getSocRolloverResultItem(String id, ContextInfo context) throws
435             DoesNotExistException,
436             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
437         SocRolloverResultItemEntity entity = socRorItemDao.find(id);
438         if (null == entity) {
439             throw new DoesNotExistException(id);
440         }
441         return entity.toDto();
442     }
443 
444     @Override
445     @Transactional(readOnly = true)
446     public List<SocRolloverResultItemInfo> getSocRolloverResultItemsByResultId(String socRolloverResultId, ContextInfo context) throws
447             DoesNotExistException,
448             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
449         List<SocRolloverResultItemEntity> entities = socRorItemDao.getBySocRolloverResultId(socRolloverResultId);
450         List<SocRolloverResultItemInfo> list = new ArrayList<SocRolloverResultItemInfo>(entities.size());
451         for (SocRolloverResultItemEntity entity : entities) {
452             list.add(entity.toDto());
453         }
454         return list;
455     }
456 
457     @Override
458     @Transactional(readOnly = true)
459     public List<SocRolloverResultItemInfo> getSocRolloverResultItemsByResultIdAndSourceCourseOfferingId(String socRolloverResultId, String sourceCourseOfferingId, ContextInfo context) throws
460             DoesNotExistException,
461             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
462         List<SocRolloverResultItemEntity> entities = socRorItemDao.getBySocRolloverResultIdAndSourceCourseOfferingId(
463                 socRolloverResultId, sourceCourseOfferingId);
464         List<SocRolloverResultItemInfo> list = new ArrayList<SocRolloverResultItemInfo>(entities.size());
465         for (SocRolloverResultItemEntity entity : entities) {
466             list.add(entity.toDto());
467         }
468         return list;
469     }
470 
471     @Override
472     @Transactional(readOnly = true)
473     public List<SocRolloverResultItemInfo> getSocRolloverResultItemsByResultIdAndTargetCourseOfferingId(String socRolloverResultId, String targetCourseOfferingId, ContextInfo context) throws
474             DoesNotExistException,
475             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
476         List<SocRolloverResultItemEntity> entities = socRorItemDao.getBySocRolloverResultIdAndTargetCourseOfferingId(
477                 socRolloverResultId, targetCourseOfferingId);
478         List<SocRolloverResultItemInfo> list = new ArrayList<SocRolloverResultItemInfo>(entities.size());
479         for (SocRolloverResultItemEntity entity : entities) {
480             list.add(entity.toDto());
481         }
482         return list;
483     }
484 
485     @Override
486     @Transactional(readOnly = true)
487     public List<SocRolloverResultInfo> getSocRolloverResultsByIds(List<String> ids, ContextInfo context) throws
488             DoesNotExistException,
489             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
490         List<SocRolloverResultEntity> entities = socRorDao.findByIds(ids);
491         List<SocRolloverResultInfo> list = new ArrayList<SocRolloverResultInfo>(entities.size());
492         for (SocRolloverResultEntity entity : entities) {
493             if (entity == null) {
494                 // if one of the entities from "findByIds" is returned as null,
495                 // then one of the keys in the list was not found
496                 throw new DoesNotExistException(ids.get(list.size()));
497             }
498             list.add(entity.toDto());
499         }
500         return list;
501     }
502 
503     @Override
504     @Transactional(readOnly = true)
505     public List<SocRolloverResultItemInfo> getSocRolloverResultItemsByIds(List<String> ids, ContextInfo context) throws
506             DoesNotExistException,
507             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
508         List<SocRolloverResultItemEntity> entities = socRorItemDao.findByIds(ids);
509         List<SocRolloverResultItemInfo> list = new ArrayList<SocRolloverResultItemInfo>(entities.size());
510         for (SocRolloverResultItemEntity entity : entities) {
511             if (entity == null) {
512                 // if one of the entities from "findByIds" is returned as null,
513                 // then one of the keys in the list was not found
514                 throw new DoesNotExistException(ids.get(list.size()));
515             }
516             list.add(entity.toDto());
517         }
518         return list;
519     }
520 
521     @Override
522     @Transactional(readOnly = true)
523     public List<SocInfo> getSocsByIds(List<String> ids, ContextInfo context) throws DoesNotExistException,
524             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
525         List<SocEntity> entities = socDao.findByIds(ids);
526         List<SocInfo> list = new ArrayList<SocInfo>(entities.size());
527         for (SocEntity entity : entities) {
528             if (entity == null) {
529                 // if one of the entities from "findByIds" is returned as null,
530                 // then one of the keys in the list was not found
531                 throw new DoesNotExistException(ids.get(list.size()));
532             }
533             list.add(entity.toDto());
534         }
535         return list;
536     }
537 
538     @Override
539     @Transactional(readOnly = true)
540     public List<String> getUnpublishedActivityOfferingIdsBySoc(String socId, ContextInfo context) throws DoesNotExistException,
541             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
542         throw new OperationFailedException("Configuration error Implemented in the calculuation layer");
543     }
544 
545     @Override
546     @Transactional(readOnly = true)
547     public List<String> getUnpublishedCourseOfferingIdsBySoc(String socId, ContextInfo context) throws DoesNotExistException,
548             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
549         return this.businessLogic.getUnpublishedCourseOfferingIdsBySoc(socId, context);
550     }
551 
552     @Override
553     @Transactional(readOnly = true)
554     public List<String> getUnscheduledActivityOfferingIdsBySoc(String socId, ContextInfo context) throws DoesNotExistException,
555             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
556         return this.businessLogic.getUnscheduledActivityOfferingIdsBySoc(socId, context);
557     }
558 
559     @Override
560     @Transactional(readOnly = true)
561     public Boolean isCourseOfferingInSoc(String socId, String courseOfferingId, ContextInfo context) throws DoesNotExistException,
562             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
563         return this.businessLogic.isCourseOfferingInSoc(socId, courseOfferingId, context);
564     }
565 
566     @Override
567     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
568     public SocRolloverResultInfo reverseRollover(String rolloverResultId, List<String> optionKeys, ContextInfo context) throws
569             DoesNotExistException,
570             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
571         return this.businessLogic.reverseRollover(rolloverResultId, optionKeys, context);
572     }
573 
574     @Override
575     // Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
576     public SocInfo rolloverSoc(String sourceSocId, String targetTermId, List<String> optionKeys, ContextInfo context) throws
577             DoesNotExistException,
578             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
579         return this.businessLogic.rolloverSoc(sourceSocId, targetTermId, optionKeys, context);
580     }
581 
582     @Override
583     public StatusInfo startScheduleSoc(String socId, List<String> optionKeys, ContextInfo context) throws DoesNotExistException, InvalidParameterException,
584             MissingParameterException, OperationFailedException, PermissionDeniedException {
585 
586         return this.businessLogic.startScheduleSoc(socId, optionKeys, context);
587     }
588 
589     @Override
590     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
591     public SocInfo updateSoc(String id, SocInfo info, ContextInfo context) throws DataValidationErrorException,
592             DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
593             PermissionDeniedException, ReadOnlyException, VersionMismatchException {
594         SocEntity entity = socDao.find(id);
595         if (entity == null) {
596             throw new DoesNotExistException(id);
597         }
598         if (!entity.getSocState().equals (info.getStateKey())) {
599             throw new ReadOnlyException ("state key can only be changed by calling changeSocState");
600         }
601         entity.fromDTO(info);
602 
603         entity.setEntityUpdated(context);
604 
605         return socDao.merge(entity).toDto();
606     }
607 
608     @Override
609     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
610     public SocRolloverResultInfo updateSocRolloverProgress(String id, Integer itemsProcessed, ContextInfo context) throws
611             DataValidationErrorException,
612             DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
613             PermissionDeniedException, ReadOnlyException, VersionMismatchException {
614         SocRolloverResultEntity entity = socRorDao.find(id);
615         if (entity == null) {
616             throw new DoesNotExistException(id);
617         }
618         entity.setItemsProcessed(itemsProcessed);
619 
620         entity.setEntityUpdated(context);
621 
622         Set<SocRolloverResultAttributeEntity> resultAttributeEntities = entity.getAttributes();
623         for (SocRolloverResultAttributeEntity attr : resultAttributeEntities) {
624             if (CourseOfferingSetServiceConstants.DATE_COMPLETED_RESULT_DYNATTR_KEY.equals(attr.getKey())) {
625                 // Update the date completed
626                 attr.setValue(TransformUtility.dateTimeToDynamicAttributeString(new Date()));
627             }
628         }
629         return socRorDao.merge(entity).toDto();
630     }
631 
632     @Override
633     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
634     public SocRolloverResultInfo updateSocRolloverResult(String id, SocRolloverResultInfo info, ContextInfo context) throws
635             DataValidationErrorException,
636             DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
637             PermissionDeniedException, ReadOnlyException, VersionMismatchException {
638         SocRolloverResultEntity entity = socRorDao.find(id);
639         if (entity == null) {
640             throw new DoesNotExistException(id);
641         }
642         // remove any options that are no longer part of the group
643         // Adding additional ones is accomplished in the SockRolloverResultEntity
644         // But had to do this here because needed access to the entity manager
645         List<SocRolloverResultOptionEntity> notDeletedOptions = new ArrayList<SocRolloverResultOptionEntity>(
646                 entity.getOptions().size());
647         for (SocRolloverResultOptionEntity optionEntity : entity.getOptions()) {
648             if (!info.getOptionKeys().contains(optionEntity.getOptionId())) {
649                 socDao.getEm().remove(optionEntity);
650             } else {
651                 notDeletedOptions.add(optionEntity);
652             }
653         }
654         entity.setOptions(new HashSet<SocRolloverResultOptionEntity>(notDeletedOptions));
655         entity.fromDTO(info);
656 
657         entity.setEntityUpdated(context);
658 
659         return socRorDao.merge(entity).toDto();
660     }
661 
662     @Override
663     @Transactional(readOnly = false, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
664     public SocRolloverResultItemInfo updateSocRolloverResultItem(String id, SocRolloverResultItemInfo info, ContextInfo context) throws
665             DataValidationErrorException,
666             DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException,
667             PermissionDeniedException, ReadOnlyException, VersionMismatchException {
668         SocRolloverResultItemEntity entity = socRorItemDao.find(id);
669         if (entity == null) {
670             throw new DoesNotExistException(id);
671         }
672         entity.fromDTO(info);
673 
674         entity.setEntityUpdated(context);
675 
676         return socRorItemDao.merge(entity).toDto();
677     }
678 
679     @Override
680     public List<ValidationResultInfo> validateSoc(String validationType, SocInfo socInfo, ContextInfo context) throws
681             DoesNotExistException,
682             InvalidParameterException, MissingParameterException, OperationFailedException {
683         return new ArrayList<ValidationResultInfo>();
684     }
685 
686     @Override
687     public List<ValidationResultInfo> validateSocRolloverResult(String validationType, SocRolloverResultInfo socRolloverResultInfo, ContextInfo context) throws
688             DoesNotExistException,
689             InvalidParameterException, MissingParameterException, OperationFailedException {
690         return new ArrayList<ValidationResultInfo>();
691     }
692 
693     @Override
694     public List<ValidationResultInfo> validateSocRolloverResultItem(String validationType, SocRolloverResultItemInfo socRolloverResultItemInfo, ContextInfo context) throws
695             DoesNotExistException,
696             InvalidParameterException, MissingParameterException, OperationFailedException {
697         return new ArrayList<ValidationResultInfo>();
698     }
699 
700     @Override
701     @Transactional(readOnly = true)
702     public List<String> searchForSocRolloverResultIds(QueryByCriteria criteria,  ContextInfo context) throws
703             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException { 
704         GenericQueryResults<String> results = criteriaLookupService.lookupIds(SocRolloverResultEntity.class,
705                 criteria);
706         return results.getResults();
707     }
708 
709     @Override
710     @Transactional(readOnly = true)
711     public List<SocRolloverResultInfo> searchForSocRolloverResults(QueryByCriteria criteria,  ContextInfo context) throws
712             InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
713         GenericQueryResults<SocRolloverResultEntity> results = criteriaLookupService.lookup(SocRolloverResultEntity.class,
714                 criteria);
715         List<SocRolloverResultInfo> socRolloverResultInfos = new ArrayList<SocRolloverResultInfo>(results.getResults().size());
716         for (SocRolloverResultEntity socRolloverResult : results.getResults()) {
717             SocRolloverResultInfo socRolloverResultInfo = socRolloverResult.toDto();
718             socRolloverResultInfos.add(socRolloverResultInfo);
719         }
720         return socRolloverResultInfos;
721     }
722 
723     public void setCriteriaLookupService(CriteriaLookupService criteriaLookupService) {
724         this.criteriaLookupService = criteriaLookupService;
725     }
726 
727     @Override
728     @Transactional(readOnly = false, timeout=6168000, noRollbackFor = {DoesNotExistException.class}, rollbackFor = {Throwable.class})
729     public StatusInfo changeSocState(String socId,
730                                      String nextStateKey,
731                                      ContextInfo contextInfo)
732             throws DoesNotExistException, InvalidParameterException,
733             MissingParameterException, OperationFailedException,
734             PermissionDeniedException {
735 
736         org.springframework.transaction.PlatformTransactionManager mgr;
737 
738         SocEntity entity = socDao.find(socId);
739         if (entity == null) {
740             throw new DoesNotExistException(socId);
741         }
742         String thisStateKey = entity.getSocState();
743 
744         if(!StringUtils.isEmpty(nextStateKey) && !thisStateKey.equals(nextStateKey)){
745             //propagation
746             Map<String, StatusInfo> spStatusMap = stateTransitionsHelper.processStatePropagations(socId, thisStateKey + ":" + nextStateKey, contextInfo);
747             for (StatusInfo statusInfo : spStatusMap.values()) {
748                 if (!statusInfo.getIsSuccess()){
749                     throw new OperationFailedException(statusInfo.getMessage());
750                 }
751             }
752 
753             StatusInfo scStatus = stateTransitionsHelper.processStateConstraints(socId, nextStateKey, contextInfo);
754             if(scStatus.getIsSuccess()) {
755                 // determine if the state key given is a SOC lifecycle state or a scheduling state
756                 boolean isSchedulingState = Arrays.asList(CourseOfferingSetServiceConstants.ALL_SOC_SCHEDULING_STATES).contains(nextStateKey);
757 
758                 if(!isSchedulingState) {
759                     entity.setSocState(nextStateKey);
760                 }
761 
762                 // Log the state change
763                 logStateChange(entity, nextStateKey, contextInfo);
764                 LOG.warn(String.format("Updated SOC [%s] state to [%s].", socId, CourseOfferingSetServiceConstants.PUBLISHED_SOC_STATE_KEY));
765 
766                 entity.setEntityUpdated(contextInfo);
767                 socDao.merge(entity);
768                 socDao.getEm().flush(); // need to flush to get the version ind to update
769             }
770             else{
771                 throw new OperationFailedException(scStatus.getMessage());
772             }
773         }
774 
775         StatusInfo status = new StatusInfo ();
776         status.setSuccess(Boolean.TRUE);
777         return status;
778     }
779 
780     private void logStateChange(SocEntity entity, String stateKey, ContextInfo contextInfo) {
781         // add the state change to the log
782         // TODO: consider changing this to a call to a real logging facility instead of stuffing it in the dynamic attributes
783 
784         // Add an attribute with a key of the state en
785         AttributeInfo attr = new AttributeInfo(stateKey, DateFormatters.STATE_CHANGE_DATE_FORMATTER.format(contextInfo.getCurrentDate()));
786         entity.getAttributes().add(new SocAttributeEntity(attr, entity));
787     }
788 
789     @Override
790     public StatusInfo changeSocRolloverResultState(
791             String socId,
792             String nextStateKey,
793              ContextInfo contextInfo)
794             throws DoesNotExistException, InvalidParameterException,
795             MissingParameterException, OperationFailedException,
796             PermissionDeniedException {
797         throw new UnsupportedOperationException("To be Implemented in M5");
798     }
799 
800     @Override
801     public StatusInfo changeSocRolloverResultItemState(
802             String socId,
803             String nextStateKey,
804              ContextInfo contextInfo)
805             throws DoesNotExistException, InvalidParameterException,
806             MissingParameterException, OperationFailedException,
807             PermissionDeniedException {
808         throw new UnsupportedOperationException("To be Implemented in M5");
809     }
810 
811     @Override
812     @Transactional(readOnly = true)
813     public List<String> searchForSocRolloverResultItemIds(QueryByCriteria criteria, ContextInfo context) throws InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
814         GenericQueryResults<String> results = criteriaLookupService.lookupIds(SocRolloverResultItemEntity.class,
815                 criteria);
816         return results.getResults();
817     }
818 
819     @Override
820     @Transactional(readOnly = true)
821     public List<SocRolloverResultItemInfo> searchForSocRolloverResultItems(QueryByCriteria criteria, ContextInfo context) throws InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
822           GenericQueryResults<SocRolloverResultItemEntity> results = criteriaLookupService.lookup(SocRolloverResultItemEntity.class,
823                 criteria);
824         List<SocRolloverResultItemInfo> infos = new ArrayList<SocRolloverResultItemInfo>(results.getResults().size());
825         for (SocRolloverResultItemEntity entity : results.getResults()) {
826             SocRolloverResultItemInfo info = entity.toDto();
827             infos.add(info);
828         }
829         return infos;
830     }
831 
832     public StateService getStateService() {
833         return stateService;
834     }
835 
836     public void setStateService(StateService stateService) {
837         this.stateService = stateService;
838     }
839 }