View Javadoc
1   package org.kuali.student.enrollment.class2.courseofferingset.service.impl;
2   
3   import org.apache.commons.lang.StringUtils;
4   import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
5   import org.kuali.student.enrollment.courseoffering.service.CourseOfferingService;
6   import org.kuali.student.enrollment.courseofferingset.dto.SocInfo;
7   import org.kuali.student.enrollment.courseofferingset.service.CourseOfferingSetService;
8   import org.kuali.student.r2.common.dto.ContextInfo;
9   import org.kuali.student.r2.common.dto.StatusInfo;
10  import org.kuali.student.r2.common.exceptions.*;
11  import org.kuali.student.r2.common.util.constants.CourseOfferingServiceConstants;
12  import org.kuali.student.r2.common.util.constants.CourseOfferingSetServiceConstants;
13  import org.slf4j.Logger;
14  import org.slf4j.LoggerFactory;
15  
16  import javax.xml.namespace.QName;
17  import java.util.Date;
18  import java.util.List;
19  
20  /**
21   *  SOC mass publishing event helper.
22   *
23   *  This code needs to move to services layer ... probably to CourseOfferingSetServiceBusinessLogic.
24   *  Concurrency not addressed since it is just a stop-gap.
25   */
26  public class CourseOfferingSetPublishingHelper {
27      private static final Logger LOG = LoggerFactory.getLogger(CourseOfferingSetPublishingHelper.class);
28  
29      private CourseOfferingService coService;
30      private CourseOfferingSetService socService;
31  
32      /**
33       * Kicks off SOC lifecycle mass publishing event. Runs asychronously by default.
34       *
35       * @param socId The ID of the SOC to publish.
36       * @param optionKeys List of options.
37       * @param context
38       * @return A StatusInfo on success. Otherwise, throws and exception.
39       */
40      public StatusInfo startMassPublishingEvent(String socId, List<String> optionKeys, ContextInfo context)
41              throws InvalidParameterException, MissingParameterException, DoesNotExistException, PermissionDeniedException, OperationFailedException {
42  
43          //  Validate the SOC. Should exist, state should be "publishing" and scheduling state "completed" .
44          SocInfo soc = getSocService().getSoc(socId, context);
45          if ( ! StringUtils.equals(soc.getStateKey(), CourseOfferingSetServiceConstants.PUBLISHING_SOC_STATE_KEY)) {
46              throw new OperationFailedException(String.format("SOC state [%s] was invalid for mass publishing.", soc.getStateKey()));
47          }
48  
49          if (! StringUtils.equals(soc.getSchedulingStateKey(), CourseOfferingSetServiceConstants.SOC_SCHEDULING_STATE_COMPLETED)) {
50              throw new OperationFailedException(String.format("SOC scheduling state [%s] was invalid for mass publishing.", soc.getStateKey()));
51          }
52  
53          //  Initialize the runner
54          final SocMassPublishingRunner runner = new SocMassPublishingRunner();
55          runner.setCoService(getCourseOfferingService());
56          runner.setSocService(getSocService());
57          runner.setSocId(socId);
58          runner.setContext(context);
59  
60          if (optionKeys.contains(CourseOfferingSetServiceConstants.RUN_SYNCHRONOUSLY_OPTION_KEY)) {
61              //  Run in the existing thread.
62              runner.run();
63          } else {
64              //  Run asynchronously after any transactions clear
65              KSThreadRunnerAfterTransactionSynchronization.runAfterTransactionCompletes(runner);
66          }
67  
68          StatusInfo statusInfo = new StatusInfo();
69          statusInfo.setSuccess(true);
70          statusInfo.setMessage("Success");
71  
72          return statusInfo;
73      }
74  
75      //  For unit testing
76      public void setCoService(CourseOfferingService coService) {
77          this.coService = coService;
78      }
79  
80      public void setSocService(CourseOfferingSetService socService) {
81          this.socService = socService;
82      }
83  
84      private CourseOfferingSetService getSocService() {
85          if (socService == null) {
86              socService = (CourseOfferingSetService) GlobalResourceLoader.getService(new QName(CourseOfferingSetServiceConstants.NAMESPACE,
87                  CourseOfferingSetServiceConstants.SERVICE_NAME_LOCAL_PART));
88          }
89          return socService;
90      }
91  
92      private CourseOfferingService getCourseOfferingService() {
93          if (coService == null) {
94              coService = (CourseOfferingService) GlobalResourceLoader.getService(new QName(CourseOfferingServiceConstants.NAMESPACE,
95                      CourseOfferingServiceConstants.SERVICE_NAME_LOCAL_PART));
96          }
97          return coService;
98      }
99  
100     /**
101      * Performs publishing state changes on COs, FOs, and AOs.
102      *
103      * !!! This code should set the SOC state to a sane value: "published" on success or back to "final edits" if there is a problem. !!!
104      */
105     public class SocMassPublishingRunner implements Runnable {
106         private ContextInfo context;
107         private CourseOfferingService coService;
108         private CourseOfferingSetService socService;
109 
110         private String socId;
111 
112         @Override
113         public void run() {
114             boolean success = true;
115             try {
116                 doMpe();
117             } catch(Exception e) {
118                 LOG.error("The Mass Publishing Event did not complete successfully.", e);
119                 success = false;
120             }
121 
122             if (! success) {
123                 LOG.warn("Changing SOC state back to [{}].", CourseOfferingSetServiceConstants.FINALEDITS_SOC_STATE_KEY);
124                 StatusInfo statusInfo = null;
125                 try {
126                     statusInfo = socService.changeSocState(socId, CourseOfferingSetServiceConstants.FINALEDITS_SOC_STATE_KEY, context);
127                 } catch (Exception e) {
128                     LOG.error("Unable to change SOC state back to [{}]. The SOC state will have to be manually updated to recover.",
129                         CourseOfferingSetServiceConstants.FINALEDITS_SOC_STATE_KEY);
130                 }
131                 if (statusInfo == null || ! statusInfo.getIsSuccess()) {
132                     throw new RuntimeException(String.format("State changed failed for SOC [%s]: %s", socId, statusInfo.getMessage()));
133                 }
134             }
135         }
136 
137         private void doMpe() throws Exception {
138             LOG.warn("Beginning Mass Publishing Event for SOC [{}].", socId);
139             context.setCurrentDate(new Date());
140 
141             //  Set SOC scheduling state to "published".
142             LOG.warn("Updating SOC [{}] state to [{}].", socId, CourseOfferingSetServiceConstants.PUBLISHED_SOC_STATE_KEY);
143             context.setCurrentDate(new Date());
144             StatusInfo statusInfo = socService.changeSocState(socId, CourseOfferingSetServiceConstants.PUBLISHED_SOC_STATE_KEY, context);
145             if ( ! statusInfo.getIsSuccess()) {
146                 throw new RuntimeException(String.format("State changed failed for SOC [%s]: %s", socId, statusInfo.getMessage()));
147             }
148 
149             LOG.warn("Mass Publishing Event for SOC [{}] completed.", socId);
150         }
151 
152         public String getSocId() {
153             return socId;
154         }
155 
156         public void setSocId(String socId) {
157             this.socId = socId;
158         }
159 
160         public void setContext(ContextInfo context) {
161             this.context = context;
162         }
163 
164         public void setCoService(CourseOfferingService coService) {
165             this.coService = coService;
166         }
167 
168         public void setSocService(CourseOfferingSetService socService) {
169             this.socService = socService;
170         }
171     }
172 }