001: /*
002: * Copyright 2005-2006 The Kuali Foundation.
003: *
004: *
005: * Licensed under the Educational Community License, Version 1.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.opensource.org/licenses/ecl1.php
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package edu.iu.uis.eden.web;
018:
019: import java.util.ArrayList;
020: import java.util.Iterator;
021: import java.util.List;
022: import java.util.Map;
023:
024: import javax.servlet.http.HttpServletRequest;
025: import javax.servlet.http.HttpServletResponse;
026:
027: import org.apache.struts.action.ActionErrors;
028: import org.apache.struts.action.ActionForm;
029: import org.apache.struts.action.ActionForward;
030: import org.apache.struts.action.ActionMapping;
031: import org.apache.struts.action.ActionMessages;
032: import org.apache.struts.actions.DispatchAction;
033:
034: import edu.iu.uis.eden.EdenConstants;
035: import edu.iu.uis.eden.KEWServiceLocator;
036: import edu.iu.uis.eden.WorkflowServiceErrorException;
037: import edu.iu.uis.eden.WorkflowServiceErrorImpl;
038: import edu.iu.uis.eden.clientapp.WorkflowDocument;
039: import edu.iu.uis.eden.clientapp.WorkflowInfo;
040: import edu.iu.uis.eden.clientapp.vo.AdHocRevokeVO;
041: import edu.iu.uis.eden.clientapp.vo.NetworkIdVO;
042: import edu.iu.uis.eden.clientapp.vo.RouteNodeInstanceVO;
043: import edu.iu.uis.eden.clientapp.vo.WorkgroupNameIdVO;
044: import edu.iu.uis.eden.exception.EdenUserNotFoundException;
045: import edu.iu.uis.eden.exception.WorkflowException;
046: import edu.iu.uis.eden.exception.WorkflowRuntimeException;
047: import edu.iu.uis.eden.export.ExportDataSet;
048: import edu.iu.uis.eden.export.web.ExportServlet;
049: import edu.iu.uis.eden.routeheader.DocumentRouteHeaderValue;
050: import edu.iu.uis.eden.user.AuthenticationUserId;
051: import edu.iu.uis.eden.user.UserService;
052: import edu.iu.uis.eden.web.session.UserSession;
053: import edu.iu.uis.eden.workgroup.GroupNameId;
054: import edu.iu.uis.eden.workgroup.WorkgroupService;
055:
056: /**
057: * An abstract super class for all Struts Actions in KEW. Adds some custom
058: * dispatch behavior by extending the Struts DispatchAction.
059: *
060: * @author bmcgough
061: * @author rkirkend
062: */
063: public abstract class WorkflowAction extends DispatchAction {
064:
065: private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
066: .getLogger(WorkflowAction.class);
067:
068: public ActionForward execute(ActionMapping mapping,
069: ActionForm form, HttpServletRequest request,
070: HttpServletResponse response) throws Exception {
071: try {
072: request.setAttribute("Constants", new EdenConstants());
073: request.setAttribute("UrlResolver", UrlResolver
074: .getInstance());
075: ActionMessages messages = null;
076: messages = establishRequiredState(request, form);
077: if (messages != null && !messages.isEmpty()) {
078: // XXX: HACK: FIXME:
079: // obviously this implies that we can't return both ActionErrors
080: // and ActionMessages... :(
081: // probably establishRequiredState should be refactored to have
082: // a generic 'should-we-continue'
083: // boolean return, so that control flow can be more explicitly
084: // specified by the subclass
085: if (messages instanceof ActionErrors) {
086: saveErrors(request, (ActionErrors) messages);
087: } else {
088: saveMessages(request, messages);
089: }
090: return mapping.findForward("requiredStateError");
091: }
092: LOG.info(request.getQueryString());
093: ActionForward returnForward = null;
094:
095: if (request.getParameterMap() != null) {
096: for (Iterator iter = request.getParameterMap()
097: .entrySet().iterator(); iter.hasNext();) {
098: String parameterName = (String) ((Map.Entry) iter
099: .next()).getKey();
100: if (parameterName.startsWith("methodToCall.")
101: && parameterName.endsWith(".x")) {
102: String methodToCall = parameterName
103: .substring(parameterName
104: .indexOf("methodToCall.") + 13,
105: parameterName.lastIndexOf(".x"));
106: if (methodToCall != null
107: && methodToCall.length() > 0) {
108: returnForward = this .dispatchMethod(
109: mapping, form, request, response,
110: methodToCall);
111: }
112: }
113: }
114: }
115: if (returnForward == null) {
116: if (request.getParameter("methodToCall") != null
117: && !"".equals(request
118: .getParameter("methodToCall"))
119: && !"execute".equals(request
120: .getParameter("methodToCall"))) {
121: LOG.info("dispatch to methodToCall "
122: + request.getParameter("methodToCall")
123: + " called");
124: returnForward = super .execute(mapping, form,
125: request, response);
126: } else {
127: LOG.info("dispatch to default start methodToCall");
128: returnForward = start(mapping, form, request,
129: response);
130: }
131: }
132:
133: messages = establishFinalState(request, form);
134: if (messages != null && !messages.isEmpty()) {
135: saveMessages(request, messages);
136: return mapping.findForward("finalStateError");
137: }
138: return returnForward;
139: } catch (Exception e) {
140: LOG
141: .error("Error processing action "
142: + mapping.getPath(), e);
143: throw new WorkflowRuntimeException(e);
144: }
145: }
146:
147: public abstract ActionForward start(ActionMapping mapping,
148: ActionForm form, HttpServletRequest request,
149: HttpServletResponse response) throws Exception;
150:
151: public ActionForward refresh(ActionMapping mapping,
152: ActionForm form, HttpServletRequest request,
153: HttpServletResponse response) throws Exception {
154: return start(mapping, form, request, response);
155: }
156:
157: public abstract ActionMessages establishRequiredState(
158: HttpServletRequest request, ActionForm form)
159: throws Exception;
160:
161: public ActionMessages establishFinalState(
162: HttpServletRequest request, ActionForm form)
163: throws Exception {
164: return null;
165: }
166:
167: protected ActionForward exportDataSet(HttpServletRequest request,
168: ExportDataSet dataSet) {
169: request.getSession().setAttribute(
170: ExportServlet.EXPORT_DATA_SET_KEY, dataSet);
171: return new ActionForward(ExportServlet.generateExportPath(
172: request, dataSet), true);
173: }
174:
175: public static UserSession getUserSession(HttpServletRequest request) {
176: return UserLoginFilter.getUserSession(request);
177: }
178:
179: public boolean isEmpty(String propertyValue) {
180: if (propertyValue == null || propertyValue.trim().equals("")) {
181: return true;
182: }
183: return false;
184: }
185:
186: public ActionForward performLookup(ActionMapping mapping,
187: ActionForm form, HttpServletRequest request,
188: HttpServletResponse response) throws Exception {
189: String basePath = request.getScheme() + "://"
190: + request.getServerName() + ":"
191: + request.getServerPort() + request.getContextPath()
192: + mapping.getModuleConfig().getPrefix();
193: String lookupUrl = basePath
194: + "/Lookup.do?methodToCall=start&docFormKey="
195: + getUserSession(request).addObject(form)
196: + "&lookupableImplServiceName="
197: + request.getParameter("lookupableImplServiceName")
198: + "&returnLocation=" + basePath + mapping.getPath()
199: + ".do";
200: return new ActionForward(lookupUrl, true);
201: }
202:
203: public ActionForward routeToAppSpecificRecipient(
204: ActionMapping mapping, ActionForm form,
205: HttpServletRequest request, HttpServletResponse response)
206: throws Exception {
207: WorkflowRoutingForm routingForm = (WorkflowRoutingForm) form;
208: AppSpecificRouteRecipient recipient = routingForm
209: .getAppSpecificRouteRecipient();
210: recipient.setActionRequested(routingForm
211: .getAppSpecificRouteActionRequestCd());
212: recipient.setType(routingForm
213: .getAppSpecificRouteRecipientType());
214: validateAppSpecificRoute(recipient);
215:
216: try {
217: String routeNodeName = getAdHocRouteNodeName(routingForm
218: .getFlexDoc().getRouteHeaderId());
219: if (EdenConstants.PERSON.equals(recipient.getType())) {
220: routingForm.getFlexDoc()
221: .appSpecificRouteDocumentToUser(
222: recipient.getActionRequested(),
223: routeNodeName,
224: routingForm.getAnnotation(),
225: new NetworkIdVO(recipient.getId()), "",
226: true);
227: } else {
228: routingForm
229: .getFlexDoc()
230: .appSpecificRouteDocumentToWorkgroup(
231: recipient.getActionRequested(),
232: routeNodeName,
233: routingForm.getAnnotation(),
234: new WorkgroupNameIdVO(recipient.getId()),
235: "", true);
236: }
237: routingForm.getAppSpecificRouteList().add(recipient);
238: routingForm.resetAppSpecificRoute();
239: } catch (Exception e) {
240: LOG.error("Error generating app specific route request", e);
241: throw new WorkflowServiceErrorException(
242: "AppSpecific Route Error",
243: new WorkflowServiceErrorImpl(
244: "AppSpecific Route Error",
245: "appspecificroute.systemerror"));
246: }
247: return mapping.getInputForward();
248: }
249:
250: public ActionForward noOp(ActionMapping mapping, ActionForm form,
251: HttpServletRequest request, HttpServletResponse response)
252: throws Exception {
253: return mapping.findForward("basic");
254: }
255:
256: protected String getAdHocRouteNodeName(Long routeHeaderId)
257: throws WorkflowException {
258: WorkflowInfo info = new WorkflowInfo();
259: RouteNodeInstanceVO[] nodeInstances = info
260: .getActiveNodeInstances(routeHeaderId);
261: if (nodeInstances == null || nodeInstances.length == 0) {
262: nodeInstances = info
263: .getTerminalNodeInstances(routeHeaderId);
264: }
265: if (nodeInstances == null || nodeInstances.length == 0) {
266: throw new WorkflowException(
267: "Could not locate a node on the document to send the ad hoc request to.");
268: }
269: return nodeInstances[0].getName();
270: }
271:
272: protected void validateAppSpecificRoute(
273: AppSpecificRouteRecipient recipient) {
274: List messages = new ArrayList();
275: if (recipient.getId() == null
276: || recipient.getId().trim().equals("")) {
277: messages.add(new WorkflowServiceErrorImpl(
278: "AppSpecific Recipient empty",
279: "appspecificroute.recipient.required"));
280: }
281:
282: if (EdenConstants.PERSON.equals(recipient.getType())
283: && recipient.getId() != null
284: && !recipient.getId().trim().equals("")) {
285: try {
286: getUserService().getWorkflowUser(
287: new AuthenticationUserId(recipient.getId()));
288: } catch (EdenUserNotFoundException e) {
289: LOG.error("App Specific user recipient not found", e);
290: messages.add(new WorkflowServiceErrorImpl(
291: "AppSpecific Recipient invalid",
292: "appspecificroute.user.invalid"));
293: }
294: }
295:
296: if (EdenConstants.WORKGROUP.equals(recipient.getType())
297: && recipient.getId() != null
298: && !recipient.getId().trim().equals("")) {
299: if (getWorkgroupService().getWorkgroup(
300: new GroupNameId(recipient.getId())) == null) {
301: messages.add(new WorkflowServiceErrorImpl(
302: "AppSpecific Recipient workgroup invalid",
303: "appspecificroute.workgroup.invalid"));
304: }
305: }
306:
307: if (!messages.isEmpty()) {
308: throw new WorkflowServiceErrorException(
309: "AppSpecific Route validation Error", messages);
310: }
311:
312: }
313:
314: private UserService getUserService() {
315: return (UserService) KEWServiceLocator
316: .getService(KEWServiceLocator.USER_SERVICE);
317: }
318:
319: private WorkgroupService getWorkgroupService() {
320: return (WorkgroupService) KEWServiceLocator
321: .getService(KEWServiceLocator.WORKGROUP_SRV);
322: }
323:
324: public ActionForward cancelDocument(ActionMapping mapping,
325: ActionForm form, HttpServletRequest request,
326: HttpServletResponse response) throws Exception {
327: WorkflowRoutingForm routingForm = (WorkflowRoutingForm) form;
328: DocumentRouteHeaderValue routeHeader = KEWServiceLocator
329: .getRouteHeaderService().getRouteHeader(
330: routingForm.getDocId());
331: KEWServiceLocator.getWorkflowDocumentService().cancelDocument(
332: getUserSession(request).getWorkflowUser(), routeHeader,
333: routingForm.getAnnotation());
334: request.setAttribute("routeHeaderId", routingForm.getDocId());
335: return mapping.findForward("DeleteRouteHeaderConfirmation");
336: }
337:
338: public ActionForward removeAppSpecificRecipient(
339: ActionMapping mapping, ActionForm form,
340: HttpServletRequest request, HttpServletResponse response)
341: throws Exception {
342: WorkflowRoutingForm routingForm = (WorkflowRoutingForm) form;
343: int removedIndex = new Integer(routingForm
344: .getRemovedAppSpecificRecipient()).intValue();
345: AppSpecificRouteRecipient recipient = (AppSpecificRouteRecipient) routingForm
346: .getAppSpecificRouteList().get(removedIndex);
347: WorkflowDocument document = routingForm.getFlexDoc();
348: List revocations = new ArrayList();
349: String[] nodeNames = document.getNodeNames();
350: for (int index = 0; index < nodeNames.length; index++) {
351: String nodeName = nodeNames[index];
352: AdHocRevokeVO revoke = new AdHocRevokeVO(nodeName);
353: if (recipient.getType().equals("person")) {
354: revoke.setUserId(new NetworkIdVO(recipient.getId()));
355: } else if (recipient.getType().equals("workgroup")) {
356: revoke.setWorkgroupId(new WorkgroupNameIdVO(recipient
357: .getId()));
358: }
359: revocations.add(revoke);
360: }
361: for (Iterator iterator = revocations.iterator(); iterator
362: .hasNext();) {
363: AdHocRevokeVO revoke = (AdHocRevokeVO) iterator.next();
364: // TODO print better message here
365: document.revokeAdHocRequests(revoke,
366: "Removed ad hoc recipient from node '"
367: + revoke.getNodeName() + "'.");
368: }
369: routingForm.getAppSpecificRouteList().remove(removedIndex);
370: routingForm.setRemovedAppSpecificRecipient(null);
371: return mapping.getInputForward();
372: }
373:
374: }
|