| 1 18 package org.apache.beehive.netui.pageflow; 19 20 import org.apache.beehive.netui.core.urls.MutableURI; 21 import org.apache.beehive.netui.pageflow.config.PageFlowActionMapping; 22 import org.apache.beehive.netui.pageflow.handler.ActionForwardHandler; 23 import org.apache.beehive.netui.pageflow.handler.ExceptionsHandler; 24 import org.apache.beehive.netui.pageflow.handler.FlowControllerHandlerContext; 25 import org.apache.beehive.netui.pageflow.handler.Handlers; 26 import org.apache.beehive.netui.pageflow.handler.LoginHandler; 27 import org.apache.beehive.netui.pageflow.internal.AdapterManager; 28 import org.apache.beehive.netui.pageflow.internal.InternalConstants; 29 import org.apache.beehive.netui.pageflow.internal.InternalExpressionUtils; 30 import org.apache.beehive.netui.pageflow.internal.InternalUtils; 31 import org.apache.beehive.netui.pageflow.scoping.ScopedRequest; 32 import org.apache.beehive.netui.util.internal.FileUtils; 33 import org.apache.beehive.netui.util.internal.InternalStringBuilder; 34 import org.apache.beehive.netui.util.internal.cache.ClassLevelCache; 35 import org.apache.beehive.netui.util.logging.Logger; 36 import org.apache.struts.Globals; 37 import org.apache.struts.action.ActionForm; 38 import org.apache.struts.action.ActionForward; 39 import org.apache.struts.action.ActionMapping; 40 import org.apache.struts.action.ActionMessage; 41 import org.apache.struts.action.ActionMessages; 42 import org.apache.struts.action.ActionServlet; 43 import org.apache.struts.action.RequestProcessor; 44 import org.apache.struts.config.ActionConfig; 45 import org.apache.struts.config.ControllerConfig; 46 import org.apache.struts.config.ModuleConfig; 47 import org.apache.struts.util.MessageResources; 48 import org.apache.struts.util.RequestUtils; 49 import org.apache.struts.util.TokenProcessor; 50 51 import javax.security.auth.login.LoginException ; 52 import javax.servlet.ServletContext ; 53 import javax.servlet.ServletException ; 54 import javax.servlet.http.HttpServletRequest ; 55 import javax.servlet.http.HttpServletResponse ; 56 import javax.servlet.http.HttpSession ; 57 import javax.sql.DataSource ; 58 import java.io.IOException ; 59 import java.lang.reflect.Field ; 60 import java.lang.reflect.InvocationTargetException ; 61 import java.lang.reflect.Method ; 62 import java.net.URISyntaxException ; 63 import java.util.ArrayList ; 64 import java.util.Iterator ; 65 import java.util.Locale ; 66 import java.util.Map ; 67 68 69 70 73 public abstract class FlowController extends PageFlowManagedObject 74 implements PageFlowConstants, ActionResolver 75 { 76 private static final Logger _log = Logger.getInstance( FlowController.class ); 77 78 private static final String ONCREATE_EXCEPTION_FORWARD = InternalConstants.ATTR_PREFIX + "onCreateException"; 79 private static final String CACHEID_ACTION_METHODS = InternalConstants.ATTR_PREFIX + "actionMethods"; 80 private static final int DEFAULT_MAX_CONCURRENT_REQUEST_COUNT = 4; 81 private static final String MAX_CONCURRENT_REQUESTS_PARAM = "pageflow-max-concurrent-requests"; 82 private static final int EXCEEDED_MAX_CONCURRENT_REQUESTS_ERRORCODE = 503; 83 private static final Locale DEFAULT_LOCALE = Locale.getDefault(); 84 private static final ActionForward NULL_ACTION_FORWARD = new ActionForward(); 85 private static final TokenProcessor TOKEN_PROCESSOR = TokenProcessor.getInstance(); 86 87 92 protected static Locale defaultLocale = DEFAULT_LOCALE; 93 94 95 102 protected transient ActionServlet servlet = null; 103 104 105 static class PerRequestState 106 { 107 private HttpServletRequest _request; 108 private HttpServletResponse _response; 109 private ActionMapping _actionMapping; 110 111 public PerRequestState( HttpServletRequest request, HttpServletResponse response, ActionMapping actionMapping ) 112 { 113 _request = request; 114 _response = response; 115 _actionMapping = actionMapping; 116 } 117 118 public HttpServletRequest getRequest() 119 { 120 return _request; 121 } 122 123 public HttpServletResponse getResponse() 124 { 125 return _response; 126 } 127 128 public ActionMapping getActionMapping() 129 { 130 return _actionMapping; 131 } 132 } 133 134 137 private transient PerRequestState _perRequestState; 138 139 142 private transient ModuleConfig _moduleConfig = null; 143 144 147 private transient int _requestCount = 0; 148 149 152 private static int _maxConcurrentRequestCount = -1; 153 154 155 158 protected FlowController() 159 { 160 } 161 162 165 public void reinitialize( HttpServletRequest request, HttpServletResponse response, ServletContext servletContext ) 166 { 167 super.reinitialize( request, response, servletContext ); 172 initModuleConfig( servletContext, request ); 173 servlet = getServlet(); 174 } 175 176 184 public void login( String username, String password ) 185 throws LoginException  186 { 187 LoginHandler lh = Handlers.get( getServletContext() ).getLoginHandler(); 188 lh.login( getHandlerContext(), username, password ); 189 } 190 191 200 public void logout( boolean invalidateSessions ) 201 { 202 LoginHandler lh = Handlers.get( getServletContext() ).getLoginHandler(); 203 lh.logout( getHandlerContext(), invalidateSessions ); 204 } 205 206 213 protected void sendError( String errText, HttpServletResponse response ) 214 throws IOException  215 { 216 sendError( errText, null, response ); 217 } 218 219 225 protected void sendError( String errText, HttpServletRequest request, HttpServletResponse response ) 226 throws IOException  227 { 228 InternalUtils.sendError( "PageFlow_Custom_Error", null, request, response, 229 new Object []{ getDisplayName(), errText } ); 230 } 231 232 243 public synchronized ActionForward handleException( Throwable ex, ActionMapping mapping, 244 ActionForm form, HttpServletRequest request, 245 HttpServletResponse response ) 246 throws IOException , ServletException  247 { 248 PerRequestState prevState = setPerRequestState( new PerRequestState( request, response, mapping ) ); 249 250 try 251 { 252 ExceptionsHandler eh = Handlers.get( getServletContext() ).getExceptionsHandler(); 253 FlowControllerHandlerContext context = getHandlerContext(); 254 255 Throwable unwrapped = eh.unwrapException( context, ex ); 257 eh.exposeException( context, unwrapped, mapping ); 258 return eh.handleException( context, unwrapped, mapping, form ); 259 } 260 finally 261 { 262 setPerRequestState( prevState ); 263 } 264 } 265 266 276 277 protected String getCurrentActionName() 278 { 279 return InternalUtils.getActionName( getActionMapping() ); 280 } 281 282 292 public ActionForward execute( ActionMapping mapping, ActionForm form, HttpServletRequest request, 293 HttpServletResponse response ) 294 throws Exception  295 { 296 if ( incrementRequestCount( request, response, getServletContext() ) ) 301 { 302 try 303 { 304 synchronized ( this ) 305 { 306 return internalExecute( mapping, form, request, response ); 307 } 308 } 309 finally 310 { 311 decrementRequestCount( request ); 312 } 313 } 314 else 315 { 316 return null; } 318 } 319 320 323 protected ActionForward internalExecute( ActionMapping mapping, ActionForm form, HttpServletRequest request, 324 HttpServletResponse response ) 325 throws Exception  326 { 327 ServletContainerAdapter sca = AdapterManager.getServletContainerAdapter( getServletContext() ); 328 PageFlowEventReporter eventReporter = sca.getEventReporter(); 329 eventReporter.actionRaised( this, mapping, form, request, response ); 330 long startTime = System.currentTimeMillis(); 331 332 ActionForward onCreateFwd = ( ActionForward ) request.getAttribute( ONCREATE_EXCEPTION_FORWARD ); 336 337 if ( onCreateFwd != null ) 338 { 339 return onCreateFwd == NULL_ACTION_FORWARD ? null : onCreateFwd; 340 } 341 342 343 PageFlowUtils.setActionURI( request ); 344 345 boolean gotPastBeforeAction = false; 350 ServletContext servletContext = getServletContext(); 351 PerRequestState prevState = setPerRequestState( new PerRequestState( request, response, mapping ) ); 352 353 try 354 { 355 beforeAction(); 359 gotPastBeforeAction = true; 360 361 PageFlowActionMapping pfActionMapping = 362 mapping instanceof PageFlowActionMapping ? ( PageFlowActionMapping ) mapping : null; 363 Object unwrappedForm = InternalUtils.unwrapFormBean( form ); 364 365 if ( unwrappedForm != null && pfActionMapping != null ) 370 { 371 if ( pfActionMapping.isOverloaded() ) 372 { 373 String mappingPath = pfActionMapping.getPath(); 374 375 for ( Class i = unwrappedForm.getClass(); i != null; i = i.getSuperclass() ) 379 { 380 String formQualifiedActionPath = getFormQualifiedActionPath( i, mappingPath ); 381 ActionConfig cf = pfActionMapping.getModuleConfig().findActionConfig( formQualifiedActionPath ); 382 383 if ( cf != null ) 384 { 385 assert cf instanceof PageFlowActionMapping : cf.getClass().getName(); 386 387 if ( _log.isDebugEnabled() ) 388 { 389 _log.debug( "Found form-specific mapping " + cf.getPath() + 390 " -- choosing this one over current mapping " + mappingPath ); 391 } 392 393 pfActionMapping = ( PageFlowActionMapping ) cf; 394 mapping = pfActionMapping; 395 break; 396 } 397 } 398 } 399 } 400 401 String actionName = InternalUtils.getActionName( mapping ); 402 403 LoginHandler loginHandler = Handlers.get( getServletContext() ).getLoginHandler(); 407 408 if ( pfActionMapping != null && pfActionMapping.isLoginRequired() 409 && loginHandler.getUserPrincipal( getHandlerContext() ) == null ) 410 { 411 NotLoggedInException ex = createNotLoggedInException( actionName, request ); 412 return handleException( ex, mapping, form, request, response ); 413 } 414 415 ActionForward retVal; 419 if ( pfActionMapping != null && pfActionMapping.isSimpleAction() ) 420 { 421 retVal = handleSimpleAction( pfActionMapping, form, request, servletContext ); 422 } 423 else 424 { 425 retVal = getActionMethodForward( actionName, unwrappedForm, request, response, mapping ); 426 } 427 428 ActionForward ret = forwardTo( retVal, mapping, request, response, actionName, null, form, servletContext ); 429 long timeTaken = System.currentTimeMillis() - startTime; 430 eventReporter.actionSuccess( this, mapping, form, request, response, ret, timeTaken ); 431 return ret; 432 } 433 catch ( Exception e ) 434 { 435 return handleException( e, mapping, form, request, response ); 443 } 444 finally 445 { 446 try 447 { 448 ActionForward overrideReturn = null; 449 450 if ( gotPastBeforeAction ) 451 { 452 try 456 { 457 afterAction(); 458 } 459 catch ( Throwable th ) 460 { 461 overrideReturn = handleException( th, mapping, form, request, response ); 462 } 463 } 464 465 savePreviousActionInfo( form, request, mapping, getServletContext() ); 469 470 if ( overrideReturn != null ) 471 { 472 return overrideReturn; 473 } 474 } 475 finally 476 { 477 setPerRequestState( prevState ); 478 } 479 } 480 } 481 482 ActionForward forwardTo( ActionForward fwd, ActionMapping mapping, HttpServletRequest request, 483 HttpServletResponse response, String actionName, ModuleConfig altModuleConfig, 484 ActionForm form, ServletContext servletContext ) 485 { 486 ActionForwardHandler handler = Handlers.get( servletContext ).getActionForwardHandler(); 490 FlowControllerHandlerContext context = new FlowControllerHandlerContext( request, response, this ); 491 return handler.doForward( context, fwd, mapping, actionName, altModuleConfig, form ); 492 } 493 494 NotLoggedInException createNotLoggedInException( String actionName, HttpServletRequest request ) 495 { 496 if ( InternalUtils.sessionExpired( request ) ) 497 { 498 return new LoginExpiredException( actionName, this ); 499 } 500 else 501 { 502 return new NotLoggedInException( actionName, this ); 503 } 504 } 505 506 509 public synchronized void create( HttpServletRequest request, HttpServletResponse response, 510 ServletContext servletContext ) 511 { 512 PerRequestState prevState = setPerRequestState( new PerRequestState( request, response, null ) ); 513 514 try 515 { 516 try 517 { 518 super.create( request, response, servletContext ); 519 } 520 catch ( Throwable th ) 521 { 522 try 523 { 524 _log.info( "Handling exception in onCreate(), FlowController " + this, th ); 525 ActionForward fwd = handleException( th, null, null, request, response ); 526 if ( fwd == null ) fwd = NULL_ACTION_FORWARD; 527 request.setAttribute( ONCREATE_EXCEPTION_FORWARD, fwd ); 528 } 529 catch ( Exception e ) 530 { 531 _log.error( "Exception thrown while handling exception in onCreate(): " + e.getMessage(), th ); 532 } 533 } 534 } 535 finally 536 { 537 setPerRequestState( prevState ); 538 } 539 540 PageFlowEventReporter er = AdapterManager.getServletContainerAdapter( servletContext ).getEventReporter(); 541 er.flowControllerCreated( this, request, response ); 542 } 543 544 548 void destroy( HttpSession session ) 549 { 550 onDestroy(); super.destroy( session ); 552 553 ServletContext servletContext = getServletContext(); 558 if ( servletContext == null && session != null ) servletContext = session.getServletContext(); 559 560 if ( servletContext != null ) 561 { 562 PageFlowEventReporter er = AdapterManager.getServletContainerAdapter( servletContext ).getEventReporter(); 563 er.flowControllerDestroyed( this ); 564 } 565 } 566 567 572 public abstract String getModulePath(); 573 574 581 protected synchronized void beforeAction() 582 throws Exception  583 { 584 } 585 586 593 protected synchronized void afterAction() 594 throws Exception  595 { 596 } 597 598 602 protected void onCreate() 603 throws Exception  604 { 605 } 606 607 620 protected void onDestroy() 621 { 622 } 623 624 634 protected void onDestroy( HttpSession session ) 635 { 636 } 637 638 642 public abstract PreviousPageInfo getPreviousPageInfoLegacy( PageFlowController curJpf, HttpServletRequest request ); 643 644 652 protected Method getActionMethod( String methodName, Class argType ) 653 { 654 String cacheKey = argType != null ? methodName + '/' + argType.getName() : methodName; 655 Class thisClass = getClass(); 656 ClassLevelCache cache = ClassLevelCache.getCache( thisClass ); 657 Method actionMethod = ( Method ) cache.get( CACHEID_ACTION_METHODS, cacheKey ); 658 659 if ( actionMethod != null ) 660 { 661 return actionMethod; 662 } 663 else 664 { 665 if ( argType == null ) 669 { 670 actionMethod = InternalUtils.lookupMethod( thisClass, methodName, null ); 674 } 675 else 676 { 677 while ( argType != null ) 682 { 683 actionMethod = InternalUtils.lookupMethod( thisClass, methodName, new Class []{ argType } ); 684 685 if ( actionMethod != null ) 686 { 687 break; 688 } 689 690 argType = argType.getSuperclass(); 691 } 692 } 693 694 if ( actionMethod != null && actionMethod.getReturnType().equals( Forward.class ) ) 695 { 696 actionMethod.setAccessible( true ); 697 cache.put( CACHEID_ACTION_METHODS, cacheKey, actionMethod ); 698 return actionMethod; 699 } 700 } 701 702 return null; 703 } 704 705 private Class getFormClass( Object form, ActionMapping mapping, HttpServletRequest request ) 706 throws ClassNotFoundException  707 { 708 if ( mapping instanceof PageFlowActionMapping ) 709 { 710 String formClassName = ( ( PageFlowActionMapping ) mapping ).getFormClass(); 711 712 if ( formClassName != null ) 713 { 714 return InternalUtils.getReloadableClass( formClassName, getServletContext() ); 715 } 716 } 717 718 return form != null ? form.getClass() : null; 719 } 720 721 734 ActionForward getActionMethodForward( String actionName, Object inputForm, 735 HttpServletRequest request, HttpServletResponse response, 736 ActionMapping mapping ) 737 throws Exception  |