View Javadoc

1   package org.kuali.student.lum.workflow;
2   
3   import java.util.Iterator;
4   import java.util.List;
5   
6   import org.kuali.student.common.dto.DtoConstants;
7   import org.kuali.student.common.dto.StatusInfo;
8   import org.kuali.student.common.exceptions.CircularReferenceException;
9   import org.kuali.student.common.exceptions.DataValidationErrorException;
10  import org.kuali.student.common.exceptions.DoesNotExistException;
11  import org.kuali.student.common.exceptions.InvalidParameterException;
12  import org.kuali.student.common.exceptions.MissingParameterException;
13  import org.kuali.student.common.exceptions.OperationFailedException;
14  import org.kuali.student.common.exceptions.PermissionDeniedException;
15  import org.kuali.student.common.exceptions.VersionMismatchException;
16  import org.kuali.student.common.versionmanagement.dto.VersionDisplayInfo;
17  import org.kuali.student.core.statement.dto.StatementTreeViewInfo;
18  import org.kuali.student.lum.course.dto.CourseInfo;
19  import org.kuali.student.lum.course.service.CourseService;
20  import org.kuali.student.lum.course.service.CourseServiceConstants;
21  import org.springframework.transaction.annotation.Transactional;
22  
23  @Transactional(noRollbackFor = { DoesNotExistException.class }, rollbackFor = { Throwable.class })
24  public class CourseStateChangeServiceImpl {
25  	private CourseService courseService;
26  
27  	/**
28  	 * Change the state of a course to a new state
29  	 * 
30  	 * @param courseId id of course
31  	 * @param newState the new state for the course
32  	 * @param prevEndTermAtpId the current version end date of course
33  	 * @return
34  	 * @throws Exception
35  	 */
36  	public StatusInfo changeState(String courseId, String newState,	String prevEndTermAtpId) throws Exception {
37  
38  		CourseInfo courseInfo = courseService.getCourse(courseId);
39  
40  		StatusInfo ret = new StatusInfo();
41  		try {
42  			if (newState.equals(DtoConstants.STATE_ACTIVE)) {
43  				if(courseInfo.isPilotCourse()){
44  					//Pilot courses get Retired
45  					//Add required fields for Retired State
46  					courseInfo.getAttributes().put("retirementRationale", "Pilot Course");
47  					courseInfo.getAttributes().put("lastTermOffered", courseInfo.getEndTerm());
48  					courseInfo.setState(DtoConstants.STATE_ACTIVE);
49  					retireCourse(courseInfo);
50  				}else{
51  					activateCourse(courseInfo, prevEndTermAtpId);
52  				}
53  			} else if (newState.equals(DtoConstants.STATE_RETIRED)) {
54  				retireCourse(courseInfo);
55  			}
56  
57  			ret.setSuccess(new Boolean(true));
58  		} catch (Exception e) {
59  			ret.setSuccess(new Boolean(false));
60  			ret.setMessage(e.getMessage());
61  		}
62  
63  		return ret;
64  	}
65  
66  	/**
67  	 * Activate a course version. Only course with a state of "Approved" can be activated.
68  	 * 
69  	 * @param courseToActivate
70  	 * @param prevEndTermAtpId the end term we set on the current version
71  	 */
72  	protected void activateCourse(CourseInfo courseToActivate, String prevEndTermAtpId) throws Exception{
73      	CourseInfo currVerCourse = getCurrentVersionOfCourse(courseToActivate);
74      	String existingState = courseToActivate.getState();
75  		String currVerState = currVerCourse.getState();
76  		boolean isCurrVer = (courseToActivate.getId().equals(currVerCourse.getId()));
77  		
78  		if (existingState.equals(DtoConstants.STATE_DRAFT)) {
79  			// since this is approved if isCurrVer we can assume there are no previously active versions to deal with
80  			if (isCurrVer) {
81  				// setstate for thisVerCourse and setCurrentVersion(courseId)
82  				updateCourseVersionStates(courseToActivate, DtoConstants.STATE_ACTIVE, currVerCourse, null, true, prevEndTermAtpId);
83  			} else if (currVerState.equals(DtoConstants.STATE_ACTIVE) ||
84  					currVerState.equals(DtoConstants.STATE_SUSPENDED)) {
85  				updateCourseVersionStates(courseToActivate, DtoConstants.STATE_ACTIVE, currVerCourse, DtoConstants.STATE_SUPERSEDED, true, prevEndTermAtpId);
86  			}
87  		}
88  	}
89  	
90  	/**
91  	 * Retire a course version. Only course with a state of "Active" or "Suspended" can be retired
92  	 * 
93  	 * @param courseToRetire the course to retire
94  	 */
95  	protected void retireCourse(CourseInfo courseToRetire) throws Exception{
96      	String existingState = courseToRetire.getState();		
97  		
98      	if (existingState.equals(DtoConstants.STATE_ACTIVE) || existingState.equals(DtoConstants.STATE_SUSPENDED)){
99      		courseToRetire.setState(DtoConstants.STATE_RETIRED);
100     		
101     		courseService.updateCourse(courseToRetire);
102 			updateStatementTreeViewInfoState(courseToRetire);    		
103     	}
104 	}
105 	
106 	/**
107 	 * Get the current version of course from another version of course
108 	 * 
109 	 * @param verIndId
110 	 */
111 	protected CourseInfo getCurrentVersionOfCourse(CourseInfo course)
112 			throws Exception {
113 		// Get version independent id of course
114 		String verIndId = course.getVersionInfo().getVersionIndId();
115 
116 		// Get id of current version of course given the versionindependen id
117 		VersionDisplayInfo curVerDisplayInfo = courseService.getCurrentVersion(
118 				CourseServiceConstants.COURSE_NAMESPACE_URI, verIndId);
119 		String curVerId = curVerDisplayInfo.getId();
120 
121 		// Return the current version of the course
122 		CourseInfo currVerCourse = courseService.getCourse(curVerId);
123 
124 		return currVerCourse;
125 	}
126 
127 	/**
128 	 * Based on null values, updates states of thisVerCourse and currVerCourse
129 	 * and sets thisVerCourse as the current version. Attempts to rollback
130 	 * transaction on exception.
131 	 * 
132 	 * @param thisVerCourse
133 	 *            this is the version that the user selected to change the state
134 	 * @param thisVerNewState
135 	 *            this is state that the user selected to change thisVerCourse
136 	 *            to
137 	 * @param currVerCourse
138 	 *            this is the current version of the course
139 	 *            (currentVersionStartDt <= now && currentVersionEndDt > now)
140 	 * @param currVerNewState
141 	 *            this is the state that we need to set the current version to.
142 	 *            Set to null to not update the currVerCourse state.
143 	 * @param makeCurrent
144 	 *            if true we'll set thisVerCourse as the current version.
145 	 * @param prevEndTermAtpId
146 	 *            the end term for the previous version to end on
147 	 * @throws Exception
148 	 */
149 	@Transactional(readOnly = false)
150 	private void updateCourseVersionStates(CourseInfo thisVerCourse,
151 			String thisVerNewState, CourseInfo currVerCourse,
152 			String currVerNewState, boolean makeCurrent,
153 			String prevEndTermAtpId) throws Exception {
154 		String thisVerPrevState = thisVerCourse.getState();
155 
156 		// if already current, will throw error if you try to make the current
157 		// version the current version.
158 		boolean isCurrent = thisVerCourse.getId().equals(currVerCourse.getId());
159 		if(!makeCurrent || !isCurrent || !thisVerCourse.getVersionInfo().getSequenceNumber().equals(1)){
160 			makeCurrent &= !isCurrent;
161 		}
162 
163 		if (thisVerNewState == null) {
164 			throw new InvalidParameterException("new state cannot be null");
165 		} else {
166 			thisVerCourse.setState(thisVerNewState);
167 			courseService.updateCourse(thisVerCourse);
168 			updateStatementTreeViewInfoState(thisVerCourse);
169 		}
170 
171 		// won't get called if previous exception was thrown
172 		if (currVerNewState != null) {
173 			currVerCourse.setState(currVerNewState);
174 			if(currVerCourse.getEndTerm()==null){
175 				currVerCourse.setEndTerm(prevEndTermAtpId);
176 			}
177 			courseService.updateCourse(currVerCourse);
178 			updateStatementTreeViewInfoState(currVerCourse);
179 		}
180 
181 		if (makeCurrent == true) {
182 			courseService.setCurrentCourseVersion(thisVerCourse.getId(),
183 					null);
184 		}
185 
186 		// for all draft and approved courses set the state to superseded.
187 		// we should only need to evaluated versions with sequence number
188 		// higher than previous active course. If the course you're
189 		// activating is the current course check all versions.
190 		if (thisVerPrevState.equals(DtoConstants.STATE_APPROVED)
191 				&& thisVerNewState.equals(DtoConstants.STATE_ACTIVE)) {
192 
193 			List<VersionDisplayInfo> versions = courseService.getVersions(
194 					CourseServiceConstants.COURSE_NAMESPACE_URI, thisVerCourse
195 							.getVersionInfo().getVersionIndId());
196 			Long startSeq = new Long(1);
197 
198 			if (!isCurrent && (currVerCourse.getId() != thisVerCourse.getId())) {
199 				startSeq = currVerCourse.getVersionInfo().getSequenceNumber() + 1;
200 			}
201 
202 			for (VersionDisplayInfo versionInfo : versions) {
203 				if (versionInfo.getSequenceNumber() >= startSeq) {
204 					CourseInfo otherCourse = courseService
205 							.getCourse(versionInfo.getId());
206 					if (otherCourse.getState().equals(
207 							DtoConstants.STATE_APPROVED)
208 							|| otherCourse.getState().equals(
209 									DtoConstants.STATE_SUBMITTED)
210 							|| otherCourse.getState().equals(
211 									DtoConstants.STATE_DRAFT)) {
212 						otherCourse.setState(DtoConstants.STATE_SUPERSEDED);
213 						courseService.updateCourse(otherCourse);
214 						updateStatementTreeViewInfoState(otherCourse);
215 					}
216 				}
217 			}
218 		}
219 
220 	}
221 
222 	public void setCourseService(CourseService courseService) {
223 		this.courseService = courseService;
224 	}
225 
226 	/**
227 	 * This method will load all the statements in a course from the course web
228 	 * service, recursively update the state of each statement in the statement
229 	 * tree, and save the update statements back to the web service.
230 	 * 
231 	 * 
232 	 * @param courseInfo
233 	 *            The course to update (call setState() in this object to set
234 	 *            the state)
235 	 * @throws DoesNotExistException
236 	 * @throws InvalidParameterException
237 	 * @throws MissingParameterException
238 	 * @throws OperationFailedException
239 	 * @throws PermissionDeniedException
240 	 * @throws DataValidationErrorException
241 	 * @throws CircularReferenceException
242 	 * @throws VersionMismatchException
243 	 */
244 	public void updateStatementTreeViewInfoState(CourseInfo courseInfo)
245 			throws DoesNotExistException, InvalidParameterException,
246 			MissingParameterException, OperationFailedException,
247 			PermissionDeniedException, DataValidationErrorException,
248 			CircularReferenceException, VersionMismatchException {
249 
250 		// Call course web service to get all requirements/statements for this
251 		// course
252 		List<StatementTreeViewInfo> statementTreeViewInfos = courseService
253 				.getCourseStatements(courseInfo.getId(), null, null);
254 
255 		// Recursively update state on all requirements/statements in the tree
256 		for (Iterator<StatementTreeViewInfo> it = statementTreeViewInfos
257 				.iterator(); it.hasNext();)
258 			StatementUtil.updateStatementTreeViewInfoState(courseInfo
259 					.getState(), it.next());
260 
261 		// Call the course web service and update the requirement/statement tree
262 		// with the new state
263 		for (Iterator<StatementTreeViewInfo> it = statementTreeViewInfos
264 				.iterator(); it.hasNext();)
265 			courseService.updateCourseStatement(courseInfo.getId(), it.next());
266 	}
267 
268 }