1 7 package com.inversoft.verge.mvc.controller.actionflow; 8 9 10 import java.util.Map ; 11 12 import javax.servlet.http.HttpServletRequest ; 13 import javax.servlet.http.HttpServletResponse ; 14 15 import org.apache.log4j.Logger; 16 17 import com.inversoft.util.StringTools; 18 import com.inversoft.verge.mvc.MVCException; 19 import com.inversoft.verge.mvc.controller.DefaultLongTxnHandler; 20 import com.inversoft.verge.mvc.controller.GenericResult; 21 import com.inversoft.verge.mvc.controller.LongTxnHandler; 22 import com.inversoft.verge.mvc.controller.LongTxnSetup; 23 import com.inversoft.verge.mvc.controller.Result; 24 import com.inversoft.verge.mvc.controller.actionflow.config.ActionFlowConfigRegistry; 25 import com.inversoft.verge.mvc.controller.actionflow.config.Link; 26 import com.inversoft.verge.mvc.controller.actionflow.config.Namespace; 27 import com.inversoft.verge.mvc.controller.actionflow.config.Node; 28 29 30 39 public class ActionFlowExecutor { 40 41 44 private static final Logger logger = Logger.getLogger(ActionFlowExecutor.class); 45 46 49 private static final String LONG_TXN_KEY = 50 "com.inversoft.verge.mvc.controller.actionflow.LongTxn"; 51 private static final Object LONG_TXN_VALUE = new Object (); 52 53 56 static volatile LongTxnHandler handler = new DefaultLongTxnHandler(); 57 58 59 62 private ActionFlowExecutor() { 63 } 65 66 67 83 public static void registerHandler(LongTxnHandler handler) { 84 ActionFlowExecutor.handler = handler; 85 } 86 87 90 public static void unregisterHandler() { 91 ActionFlowExecutor.handler = null; 92 } 93 94 121 public static Node execute(HttpServletRequest request, HttpServletResponse response, 122 String namespace, String entry, String action, Map extraParams) 123 throws ActionFlowException { 124 ActionFlowAction afAction = new ActionFlowAction(request, response); 125 afAction.setAction(action); 126 afAction.setExtraParams(extraParams); 127 128 return execute(request, response, namespace, entry, afAction); 129 } 130 131 135 protected static Node execute(HttpServletRequest request, HttpServletResponse response, 136 String namespace, String entry, ActionFlowAction action) 137 throws ActionFlowException { 138 boolean debug = logger.isDebugEnabled(); 139 assert (request != null) : "request == null"; 140 assert (response != null) : "response == null"; 141 assert (action != null) : "action == null"; 142 assert (!StringTools.isEmpty(namespace)) : "namespace is null or empty"; 143 144 ActionFlowState state = new ActionFlowState(request); 145 146 Namespace namespaceObj = 147 ActionFlowConfigRegistry.getInstance(request).lookup(namespace); 148 if (namespaceObj == null) { 149 handleException(new ActionFlowException(namespace + 150 " is not a valid namespace"), action); 151 return null; } else if (logger.isDebugEnabled()) { 153 logger.debug("Resolved namespace: " + namespace); 154 } 155 156 Node entryNode = null; 158 boolean entryEmpty = StringTools.isEmpty(entry); 159 if (entryEmpty) { 160 entryNode = state.getCurrentNodeForNamespace(namespaceObj); 161 if (debug && entryNode != null) { 162 logger.debug("No node for entry specified, using current node of: " + 163 entryNode.getName()); 164 } 165 } 166 167 if (entryNode == null) { 170 entryNode = namespaceObj.findEntry(entry); 171 if (entryNode == null) { 172 handleException(new ActionFlowException("Could not find node" + 173 " named: " + entry + " for entry and namespace: " + namespace + 174 " does not have a current node or default node"), 175 action); 176 return null; } else { 178 if (debug && entryEmpty) { 179 logger.debug("No node for entry, using default node of: " + 180 entryNode.getName()); 181 } else if (debug) { 182 logger.debug("Found node for entry or using default. Node" + 183 " resolved: " + entryNode.getName()); 184 } 185 } 186 } 187 188 state.setCurrentNodeForNamespace(namespaceObj, entryNode); 189 action.setNode(entryNode); 190 Node result = null; 191 192 try { 193 result = executeActionFlow(request, response, action, state); 194 } finally { 195 resetLongTransaction(request); 197 } 198 199 action.detachHttpServletRequest(); 201 request.setAttribute(ActionFlowConstants.ACTION_REQUEST_KEY, action); 202 203 return result; 204 } 205 206 209 protected static Node executeActionFlow(HttpServletRequest request, 210 HttpServletResponse response, ActionFlowAction action, 211 ActionFlowState state) 212 throws ActionFlowException { 213 214 assert (action != null) : "action == null"; 215 216 boolean finished = false; 217 while (!finished) { 218 if (logger.isDebugEnabled()) { 219 logger.debug("Looking up link for action: " + 220 action.getAction().toString()); 221 } 222 223 Node entry = action.getNode(); 224 Link link = entry.findLink(action.getAction()); 225 Node dest = null; 226 if (link == null) { 227 dest = findReEntryNode(action, entry.getNamespace()); 228 if (dest == null) { 229 finished = true; 230 continue; 231 } 232 } else { 233 if (logger.isDebugEnabled()) { 234 logger.debug("Found link named: " + link.getValue()); 235 } 236 237 dest = link.getDestination(); 238 } 239 240 if (entry.isLongTxnEnabled() && !dest.isLongTxnEnabled() && 244 isLongTransaction(request)) { 245 endLongTransaction(request, response, entry, action, finished); 246 finished = true; 247 continue; 248 } 249 250 action.setNode(dest); 253 254 if (!entry.isLongTxnEnabled() && dest.isLongTxnEnabled()) { 256 startLongTransaction(request, response, dest, action); 257 } 258 259 NodeExecutor executor = dest.getExecutor(); 260 Object result = null; 261 Namespace namespace = dest.getNamespace(); 262 try { 263 if (logger.isDebugEnabled()) { 264 logger.debug("Executing destination node: " + dest.getName()); 265 } 266 267 result = executor.execute(namespace, dest, action, 268 action.getExtraParams()); 269 state.setCurrentNodeForNamespace(namespace, dest); 270 271 if (dest.isExitPoint()) { 272 logger.debug("Destination is an exit node, finished"); 273 finished = true; 274 } 275 } catch (NodeExecutorException ne) { 276 handleException(new ActionFlowException(ne), action); 279 finished = true; 280 continue; 281 } catch (Exception e) { 282 if (dest.isExitPoint()) { 285 handleException(new ActionFlowException("Exit nodes can not" + 286 " throw exceptions", e), action); 287 finished = true; 288 continue; 289 } 290 291 result = e; 294 } 295 296 if (!finished && result == null) { 297 handleException(new ActionFlowException(dest.getName() + 298 " returned a null result Object and is not an exit point"), 299 action); 300 finished = true; 301 continue; 302 } 303 304 if (finished && dest.isLongTxnEnabled()) { 306 endLongTransaction(request, response, dest, action, finished); 307 finished = true; 308 } 309 310 action.setAction(result); 311 } 312 313 return action.getNode(); 314 } 315 316 322 protected static void handleException(ActionFlowException exception, 323 ActionFlowAction action) 324 throws ActionFlowException { 325 logger.error(exception.getMessage()); 327 328 ExceptionHandler handler = NodeExecutorRegistry.lookupExceptionHandler(); 331 if (handler == null) { 332 throw exception; 333 } 334 335 if (!handler.handleException(exception, action)) { 337 throw exception; 338 } 339 } 340 341 protected static Node findReEntryNode(ActionFlowAction action, 342 Namespace namespace) 343 throws ActionFlowException { 344 String actionName = ""; 347 if (action.getAction() instanceof Exception ) { 348 actionName = action.getAction().getClass().getName(); 349 } else { 350 actionName = action.getAction().toString(); 351 } 352 353 Node dest = namespace.findEntry(actionName); 357 if (dest == null) { 358 handleException(new ActionFlowException("Could not find node to " + 359 "re-enter the namespace named: " + namespace.getName() + 360 " for the action named: " + actionName), action); 361 } 362 363 if (logger.isDebugEnabled() && dest != null) { 364 logger.debug("Link not found but new destination found: " + dest); 365 } 366 367 return dest; 368 } 369 370 377 private static void startLongTransaction(HttpServletRequest request, 378 HttpServletResponse response, Node dest, ActionFlowAction action) 379 throws ActionFlowException { 380 LongTxnSetup setup = dest.getNamespace().getLongTxnSetup(); 381 String url = dest.getLongTxnStartURL(); 382 if (url == null && setup != null) { 383 url = setup.getLongTxnStartURL(action); 384 } 385 386 try { 387 handler.handleStartLongTxn(request, response, url); 388 } catch (MVCException mvce) { 389 throw new ActionFlowException(mvce.getMessage(), mvce); 390 } 391 392 request.setAttribute(LONG_TXN_KEY, LONG_TXN_VALUE); 393 } 394 395 402 private static void endLongTransaction(HttpServletRequest request, 403 HttpServletResponse response, Node node, ActionFlowAction action, 404 boolean finished) 405 throws ActionFlowException { 406 LongTxnSetup setup = node.getNamespace().getLongTxnSetup(); 407 String url = node.getLongTxnEndURL(); 408 if (url == null && setup != null) { 409 url = setup.getLongTxnEndURL(action); 410 } 411 412 String category = node.getLongTxnCategory(); 413 if (category == null) { 414 category = setup.getLongTxnURLCategory(action); 415 } 416 417 Result ret = null; 418 if (finished && node instanceof Result) { 419 ret = (Result) node; 420 ret = new GenericResult(ret.getURL(), category, false); 421 } else if (!finished) { 422 ret = new ActionFlowResult(action, node, category); 423 } else { 424 throw new ActionFlowException("Problem ending long " + 425 "transaction support. Last node was not a " + 426 "presentation or Renderable node."); 427 } 428 429 try { 432 handler.handleEndLongTxn(request, response, url, ret); 433 action.setNode(null); 434 } catch (MVCException mvce) { 435 throw new ActionFlowException(mvce.getMessage(), mvce); 436 } finally { 437 request.removeAttribute(LONG_TXN_KEY); 438 } 439 } 440 441 448 private static boolean isLongTransaction(HttpServletRequest request) { 449 return (request.getAttribute(LONG_TXN_KEY) != null); 450 } 451 452 457 private static void resetLongTransaction(HttpServletRequest request) { 458 request.removeAttribute(LONG_TXN_KEY); 459 } 460 } | Popular Tags |