001: package com.technoetic.xplanner.actions;
002:
003: import java.util.ArrayList;
004: import java.util.Collection;
005: import java.util.Collections;
006: import java.util.Date;
007: import java.util.Enumeration;
008: import java.util.Iterator;
009: import java.util.List;
010: import javax.servlet.ServletException;
011: import javax.servlet.http.HttpServletRequest;
012: import javax.servlet.http.HttpServletResponse;
013:
014: import net.sf.hibernate.Hibernate;
015: import net.sf.hibernate.Session;
016: import net.sf.hibernate.type.Type;
017: import org.apache.commons.lang.StringUtils;
018: import org.apache.log4j.Logger;
019: import org.apache.struts.Globals;
020: import org.apache.struts.action.ActionError;
021: import org.apache.struts.action.ActionErrors;
022: import org.apache.struts.action.ActionForm;
023: import org.apache.struts.action.ActionForward;
024: import org.apache.struts.action.ActionMapping;
025: import org.apache.struts.action.ActionMessages;
026: import org.springframework.beans.BeansException;
027: import org.springframework.beans.factory.BeanFactory;
028: import org.springframework.beans.factory.BeanFactoryAware;
029: import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
030:
031: import com.technoetic.xplanner.XPlannerProperties;
032: import com.technoetic.xplanner.domain.Integration;
033:
034: // todo - add history event for completed integration.
035:
036: public class IntegrationAction extends AbstractAction implements
037: BeanFactoryAware {
038: private Logger log = Logger.getLogger(getClass());
039: private static final String NOTIFICATIONS_DISABLED = "nonotify";
040:
041: private Collection listeners = Collections
042: .synchronizedList(new ArrayList());
043: private AutowireCapableBeanFactory beanFactory;
044:
045: public void init() {
046: XPlannerProperties props = new XPlannerProperties();
047: String listenersString = props
048: .getProperty("xplanner.integration.listeners");
049: if (StringUtils.isNotEmpty(listenersString)) {
050: String[] listeners = listenersString.split(",");
051: for (int i = 0; i < listeners.length; i++) {
052: try {
053: addIntegrationListener((IntegrationListener) Class
054: .forName(listeners[i]).newInstance());
055: } catch (Exception ex) {
056: log.error("error initializing listeners", ex);
057: }
058: }
059: }
060: }
061:
062: protected ActionForward doExecute(ActionMapping actionMapping,
063: ActionForm form, HttpServletRequest request,
064: HttpServletResponse reply) throws Exception {
065: try {
066: Session session = getSession(request);
067: try {
068: ActionForward forward = actionMapping
069: .findForward("display");
070: int projectId = Integer.parseInt(request
071: .getParameter("projectId"));
072: if (request.getParameter("action.join") != null) {
073: forward = onJoinRequest(session, request,
074: actionMapping, projectId);
075: } else if (isLeaveRequest(request)) {
076: onLeaveRequest(session, request, projectId);
077: } else if (request.getParameter("action.start") != null) {
078: forward = onStartRequest(session, actionMapping,
079: request, projectId);
080: } else if (request.getParameter("action.finish") != null) {
081: onFinishRequest(session, request, projectId);
082: } else if (request.getParameter("action.cancel") != null) {
083: onCancelRequest(session, request, projectId);
084: } else if (request.getParameter("personId") != null
085: && request.getParameter("comment") != null) {
086: // default if no action, <cr> in comment field instead of pressing button
087: forward = onStartRequest(session, actionMapping,
088: request, projectId);
089: }
090: session.flush();
091: session.connection().commit();
092: return addProjectId(request, forward);
093: } catch (Exception ex) {
094: session.connection().rollback();
095: return actionMapping.findForward("error");
096: }
097: } catch (Exception ex) {
098: throw new ServletException("session error", ex);
099: }
100: }
101:
102: private ActionForward addProjectId(HttpServletRequest request,
103: ActionForward forward) {
104: return new ActionForward(forward.getPath() + "?projectId="
105: + request.getParameter("projectId"), forward
106: .getRedirect());
107: }
108:
109: private ActionForward onJoinRequest(Session session,
110: HttpServletRequest request, ActionMapping actionMapping,
111: int projectId) throws Exception {
112: String personId = request.getParameter("personId");
113: String comment = request.getParameter("comment");
114:
115: if (StringUtils.isEmpty(personId) || personId.equals("0")) {
116: saveError(request, "integrations.error.noperson");
117: return actionMapping.findForward("error");
118: }
119: Integration integration = new Integration();
120: integration.setProjectId(Integer.parseInt(request
121: .getParameter("projectId")));
122: integration.setPersonId(Integer.parseInt(personId));
123: integration.setComment(comment);
124: integration.setState(Integration.PENDING);
125: integration.setWhenRequested(new Date());
126:
127: List pendingIntegrations = getIntegrationsInState(session,
128: Integration.PENDING, projectId);
129: if (pendingIntegrations.size() == 0) {
130: Integration activeIntegration = getFirstIntegrationInState(
131: session, Integration.ACTIVE, projectId);
132: if (activeIntegration == null) {
133: startIntegration(integration);
134: }
135: }
136:
137: session.save(integration);
138: return actionMapping.findForward("display");
139: }
140:
141: private void onLeaveRequest(Session session,
142: HttpServletRequest request, int projectId) throws Exception {
143: Integer oid = getLeaveOid(request);
144:
145: List pendingIntegrations = getIntegrationsInState(session,
146: Integration.PENDING, projectId);
147: Integration firstPendingIntegration = (Integration) pendingIntegrations
148: .get(0);
149:
150: Integration leavingIntegration = (Integration) session.load(
151: Integration.class, oid);
152: session.delete(leavingIntegration);
153:
154: if (isNotificationEnabled(request)
155: && leavingIntegration.getId() == firstPendingIntegration
156: .getId() && pendingIntegrations.size() > 1) {
157: fireIntegrationEvent(
158: IntegrationListener.INTEGRATION_READY_EVENT,
159: (Integration) pendingIntegrations.get(1), request);
160: }
161: }
162:
163: private Integer getLeaveOid(HttpServletRequest request) {
164: return new Integer(getLeavesParameter(request).substring(13));
165: }
166:
167: private boolean isLeaveRequest(HttpServletRequest request) {
168: return getLeavesParameter(request) != null;
169: }
170:
171: private String getLeavesParameter(HttpServletRequest request) {
172: Enumeration names = request.getParameterNames();
173: while (names.hasMoreElements()) {
174: String name = (String) names.nextElement();
175: if (name.startsWith("action.leave")) {
176: return name;
177: }
178: }
179: return null;
180: }
181:
182: private ActionForward onStartRequest(Session session,
183: ActionMapping actionMapping, HttpServletRequest request,
184: int projectId) throws Exception {
185: Integration activeIntegration = getFirstIntegrationInState(
186: session, Integration.ACTIVE, projectId);
187: if (activeIntegration == null) {
188: Integration integration = getFirstIntegrationInState(
189: session, Integration.PENDING, projectId);
190: startIntegration(integration);
191: return actionMapping.findForward("display");
192: } else {
193: saveError(request, "integrations.error.alreadyactive");
194: return actionMapping.findForward("error");
195: }
196: }
197:
198: private void saveError(HttpServletRequest request, String key) {
199: ActionErrors errors = (ActionErrors) request
200: .getAttribute(Globals.ERROR_KEY);
201: if (errors == null) {
202: errors = new ActionErrors();
203: // saveErrors() will not save an empty error collection
204: request.setAttribute(Globals.ERROR_KEY, errors);
205: }
206: errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionError(key));
207: }
208:
209: private void startIntegration(Integration integration) {
210: integration.setState(Integration.ACTIVE);
211: integration.setWhenStarted(new Date());
212: }
213:
214: private void onFinishRequest(Session session,
215: HttpServletRequest request, int projectId) throws Exception {
216: terminateIntegration(session, Integration.FINISHED, request,
217: projectId);
218: }
219:
220: private void terminateIntegration(Session session,
221: char terminalState, HttpServletRequest request,
222: int projectId) throws Exception {
223: Integration integration = getFirstIntegrationInState(session,
224: Integration.ACTIVE, projectId);
225: integration.setState(terminalState);
226: integration.setWhenComplete(new Date());
227:
228: Integration readyIntegration = getFirstIntegrationInState(
229: session, Integration.PENDING, projectId);
230: if (isNotificationEnabled(request) && readyIntegration != null) {
231: fireIntegrationEvent(
232: IntegrationListener.INTEGRATION_READY_EVENT,
233: readyIntegration, request);
234: }
235: }
236:
237: private List getIntegrationsInState(Session session, char state,
238: int projectId) throws Exception {
239: return session
240: .find(
241: "from integration in "
242: + Integration.class
243: + " where integration.state = ? and integration.projectId = ?",
244: new Object[] { new Character(state),
245: new Integer(projectId) },
246: new Type[] { Hibernate.CHARACTER,
247: Hibernate.INTEGER });
248: }
249:
250: private Integration getFirstIntegrationInState(Session session,
251: char state, int projectId) throws Exception {
252: Iterator iter = getIntegrationsInState(session, state,
253: projectId).iterator();
254: return (Integration) (iter.hasNext() ? iter.next() : null);
255: }
256:
257: private void onCancelRequest(Session session,
258: HttpServletRequest request, int projectId) throws Exception {
259: terminateIntegration(session, Integration.CANCELED, request,
260: projectId);
261: }
262:
263: public void setIntegrationListeners(ArrayList listeners) {
264: this .listeners = listeners;
265: }
266:
267: public void addIntegrationListener(IntegrationListener listener) {
268: beanFactory.autowireBeanProperties(listener,
269: AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, true);
270: listeners.add(listener);
271: }
272:
273: private void fireIntegrationEvent(int eventType,
274: Integration integration, HttpServletRequest request) {
275: Iterator iter = listeners.iterator();
276: while (iter.hasNext()) {
277: IntegrationListener listener = (IntegrationListener) iter
278: .next();
279: try {
280: listener.onEvent(eventType, integration, request);
281: } catch (Throwable ex) {
282: log.error("error dispatching integration event", ex);
283: }
284: }
285: }
286:
287: // This can be used to disable notifications during acceptance/functional testing
288: private boolean isNotificationEnabled(HttpServletRequest request) {
289: return StringUtils.isEmpty(request
290: .getParameter(NOTIFICATIONS_DISABLED))
291: && StringUtils.isEmpty((String) request
292: .getAttribute(NOTIFICATIONS_DISABLED));
293: }
294:
295: public void setBeanFactory(BeanFactory beanFactory)
296: throws BeansException {
297: this .beanFactory = (AutowireCapableBeanFactory) beanFactory;
298: }
299: }
|