1 18 package org.apache.beehive.netui.pageflow; 19 20 import org.apache.beehive.netui.util.internal.InternalStringBuilder; 21 22 import org.apache.struts.action.ActionForward; 23 import org.apache.struts.action.ActionMapping; 24 import org.apache.struts.action.ActionForm; 25 import org.apache.struts.config.ModuleConfig; 26 27 import javax.servlet.http.HttpServletRequest ; 28 import javax.servlet.ServletContext ; 29 import javax.servlet.ServletRequest ; 30 import java.util.List ; 31 import java.util.ArrayList ; 32 import java.util.Map ; 33 import java.util.HashMap ; 34 import java.lang.reflect.Field ; 35 import java.net.URI ; 36 import java.net.URL ; 37 38 import org.apache.beehive.netui.util.logging.Logger; 39 import org.apache.beehive.netui.util.internal.FileUtils; 40 import org.apache.beehive.netui.pageflow.config.PageFlowActionForward; 41 import org.apache.beehive.netui.pageflow.internal.InternalUtils; 42 import org.apache.beehive.netui.pageflow.internal.AdapterManager; 43 import org.apache.beehive.netui.pageflow.handler.ReloadableClassHandler; 44 import org.apache.beehive.netui.pageflow.handler.Handlers; 45 46 47 53 public class Forward extends ActionForward 54 { 55 private static final ActionForm[] EMPTY_ACTION_FORM_ARRAY = new ActionForm[0]; 56 57 public static final int RETURN_TO_NONE = 0; 58 public static final int RETURN_TO_CURRENT_PAGE = 1; 59 public static final int RETURN_TO_PREVIOUS_PAGE = 2; 60 public static final int RETURN_TO_PREVIOUS_ACTION = 3; 61 62 65 public static final int RETURN_TO_PAGE = -1; 66 67 70 public static final int RETURN_TO_ACTION = -2; 71 72 private static final Logger _log = Logger.getInstance( Forward.class ); 73 74 private static final String RETURN_TO_CURRENT_PAGE_STR = "currentPage"; 75 private static final String RETURN_TO_PREVIOUS_PAGE_STR = "previousPage"; 76 private static final String RETURN_TO_PAGE_LEGACY_STR = "page"; 77 private static final String RETURN_TO_PREVIOUS_ACTION_STR = "previousAction"; 78 private static final String RETURN_TO_ACTION_LEGACY_STR = "action"; 79 80 private static final Map PRIMITIVE_TYPES = new HashMap (); 81 82 static 83 { 84 PRIMITIVE_TYPES.put( "boolean", boolean.class ); 85 PRIMITIVE_TYPES.put( "byte", byte.class ); 86 PRIMITIVE_TYPES.put( "char", char.class ); 87 PRIMITIVE_TYPES.put( "double", double.class ); 88 PRIMITIVE_TYPES.put( "float", float.class ); 89 PRIMITIVE_TYPES.put( "int", int.class ); 90 PRIMITIVE_TYPES.put( "long", long.class ); 91 PRIMITIVE_TYPES.put( "short", short.class ); 92 } 93 94 private List _outputForms; 95 96 private boolean _isNestedReturn = false; 97 private boolean _init = false; 98 private transient ActionMapping _mapping = null; private transient FlowController _flowController = null; private transient ServletContext _servletContext = null; private String _mappingPath; 102 private InternalStringBuilder _queryString; 103 private boolean _explicitPath = false; 104 private String _returnFormType = null; 105 private Map _actionOutputs = null; 106 private int _returnToType; 107 private boolean _redirectSpecifiedOnAnnotation = false; 108 private boolean _redirectSetThroughMethod = false; 109 private boolean _restoreQueryString = false; 110 private boolean _externalRedirect = false; 111 112 116 private ModuleConfig _altModuleConfig; 117 118 119 122 protected Forward( Forward init ) 123 { 124 _outputForms = init._outputForms; 125 _init = init._init; 126 _mapping = init._mapping; 127 _mappingPath = init._mappingPath; 128 _queryString = init._queryString; 129 _explicitPath = init._explicitPath; 130 _flowController = init._flowController; 131 _servletContext = init._servletContext; 132 _returnFormType = init._returnFormType; 133 _actionOutputs = init._actionOutputs; 134 _returnToType = init._returnToType; 135 _restoreQueryString = init._restoreQueryString; 136 _externalRedirect = init._externalRedirect; 137 _redirectSpecifiedOnAnnotation = init._redirectSpecifiedOnAnnotation; 138 _redirectSetThroughMethod = init._redirectSetThroughMethod; 139 } 140 141 144 protected Forward( HttpServletRequest request ) 145 { 146 setPath( InternalUtils.getDecodedServletPath( request ) ); 147 setContextRelative( true ); 148 _explicitPath = true; 149 } 150 151 160 public Forward( String forwardName ) 161 { 162 setName( forwardName ); 163 } 164 165 177 public Forward( String forwardName, Object outputFormBean ) 178 { 179 this( forwardName ); 180 181 if ( outputFormBean != null ) 182 { 183 addOutputForm( outputFormBean ); 184 } 185 } 186 187 199 public Forward( String forwardName, String actionOutputName, Object actionOutputValue ) 200 { 201 this( forwardName ); 202 addActionOutput( actionOutputName, actionOutputValue ); 203 } 204 205 212 public Forward( URI uri ) 213 { 214 setPath( uri.toString() ); 215 setContextRelative( uri.getPath().startsWith( "/" ) ); 216 if ( uri.isAbsolute() ) super.setRedirect( true ); 217 _explicitPath = true; 218 } 219 220 227 public Forward( URI uri, boolean doRedirect ) 228 { 229 if ( ! doRedirect && uri.isAbsolute() ) 230 { 231 throw new IllegalStateException ( "Redirect value cannot be set to false for an absolute URI." ); 232 } 233 234 setPath( uri.toString() ); 235 setRedirect( doRedirect ); 236 setContextRelative( uri.getPath().startsWith( "/" ) ); 237 _explicitPath = true; 238 } 239 240 246 public Forward( URL url ) 247 { 248 setPath( url.toString() ); 249 super.setRedirect( true ); 250 _explicitPath = true; 251 } 252 253 257 protected Forward( ActionForward initFwd, ServletContext servletContext ) 258 { 259 _servletContext = servletContext; 260 setName( initFwd.getName() ); 261 initFrom( initFwd ); 262 } 263 264 271 public void setRedirect( boolean doRedirect ) 272 { 273 super.setRedirect( doRedirect ); 274 _redirectSetThroughMethod = true; 275 } 276 277 283 public boolean isRedirect() 284 { 285 return super.getRedirect(); 286 } 287 288 296 public final void addOutputForm( Object formBean ) 297 { 298 assert formBean != null : "The output form bean may not me null."; 299 300 if ( formBean == null ) throw new IllegalArgumentException ( "The output form bean may not be null." ); 301 if ( _outputForms == null ) _outputForms = new ArrayList (); 302 303 if ( _init && getRedirect() ) 308 { 309 String actionPath = _mappingPath != null ? _mappingPath : ""; 310 String descrip = getName() != null ? getName() : getPath(); 311 PageFlowException ex = new IllegalRedirectOutputFormException( descrip, actionPath, _flowController, 312 formBean.getClass().getName() ); 313 InternalUtils.throwPageFlowException( ex ); 314 } 315 316 _outputForms.add( InternalUtils.wrapFormBean( formBean ) ); 317 } 318 319 324 public final ActionForm[] getOutputForms() 325 { 326 if ( _outputForms == null ) return EMPTY_ACTION_FORM_ARRAY; 327 return ( ActionForm[] ) _outputForms.toArray( EMPTY_ACTION_FORM_ARRAY ); 328 } 329 330 333 public ActionForm getFirstOutputForm( HttpServletRequest request ) 334 { 335 if ( _outputForms == null || _outputForms.size() == 0 ) 336 { 337 if ( _returnFormType != null ) 338 { 339 try 340 { 341 if ( _log.isDebugEnabled() ) 342 { 343 _log.debug( "Creating form bean of type " + _returnFormType ); 344 } 345 346 ServletContext servletContext = InternalUtils.getServletContext( request ); 347 ReloadableClassHandler rch = Handlers.get( servletContext ).getReloadableClassHandler(); 348 Object formBean = rch.newInstance( _returnFormType ); 349 ActionForm wrappedFormBean = InternalUtils.wrapFormBean( formBean ); 350 addOutputForm( wrappedFormBean ); 351 return wrappedFormBean; 352 } 353 catch ( Exception e ) 354 { 355 _log.error( "Could not create form bean instance of " + _returnFormType, e ); 356 } 357 } 358 359 return null; 360 } 361 else 362 { 363 return ( ActionForm ) _outputForms.get( 0 ); 364 } 365 } 366 367 379 public boolean doesResolve() 380 { 381 if ( _explicitPath ) 382 { 383 return true; 384 } 385 386 assert _mapping != null || _altModuleConfig != null : "PageFlow.Forward.doesResolve() called outside of request"; 387 return findForward( getName() ) != null; 388 } 389 390 397 protected ActionForward findForward( String forwardName ) 398 { 399 ActionForward fwd = _mapping != null ? _mapping.findForward( forwardName ) : null; 400 401 if ( fwd != null ) 402 { 403 return fwd; 404 } 405 else if ( _altModuleConfig != null ) 406 { 407 return ( ActionForward ) _altModuleConfig.findForwardConfig( forwardName ); 408 } 409 410 return null; 411 } 412 413 419 public void setAlternateModuleConfig( ModuleConfig mc ) 420 { 421 _altModuleConfig = mc; 422 } 423 424 private final void init() 425 { 426 if ( ! _init ) 427 { 428 if ( _mappingPath == null && _altModuleConfig == null ) 429 { 430 throw new IllegalStateException ( "Forward is not initialized. Use initialize()." ); 431 } 432 433 ActionForward fwd = findForward( getName() ); 434 435 if ( fwd == null ) 436 { 437 PageFlowException ex = new UnresolvableForwardException( getName(), _mappingPath, _flowController ); 438 InternalUtils.throwPageFlowException( ex ); 439 } 440 441 initFrom( fwd ); 442 443 if ( getRedirect() ) 448 { 449 if ( _actionOutputs != null && ! _actionOutputs.isEmpty() ) 450 { 451 PageFlowException ex = 452 new IllegalActionOutputException( getName(), _mappingPath, _flowController, 453 ( String ) _actionOutputs.keySet().iterator().next() ); 454 InternalUtils.throwPageFlowException( ex ); 455 } 456 457 if ( _outputForms != null && ! _outputForms.isEmpty() ) 458 { 459 PageFlowException ex = 460 new IllegalRedirectOutputFormException( getName(), _mappingPath, _flowController, 461 _outputForms.get( 0 ).getClass().getName() ); 462 InternalUtils.throwPageFlowException( ex ); 463 } 464 } 465 } 466 } 467 468 private void initFrom( ActionForward fwd ) 469 { 470 setContextRelative( fwd.getContextRelative() ); 471 472 path = fwd.getPath(); 476 if ( _queryString != null ) path += _queryString.toString(); 477 478 if ( fwd instanceof PageFlowActionForward ) 479 { 480 PageFlowActionForward fc = ( PageFlowActionForward ) fwd; 481 _isNestedReturn = fc.isNestedReturn(); 482 _returnFormType = fc.getReturnFormType(); 483 _redirectSpecifiedOnAnnotation = fc.hasExplicitRedirectValue(); 484 _restoreQueryString = fc.isRestoreQueryString(); 485 _externalRedirect = fc.isExternalRedirect(); 486 487 Class returnFormClass = null; 488 489 if ( _returnFormType != null ) 490 { 491 try 492 { 493 returnFormClass = Class.forName( _returnFormType ); 494 } 495 catch ( ClassNotFoundException e ) 496 { 497 assert false : e; 499 } 500 } 501 502 if ( fc.isReturnToPage() || fc.isReturnToAction() ) 503 { 504 String fwdPath = fc.getPath(); 505 506 if ( fwdPath.equals( RETURN_TO_PREVIOUS_PAGE_STR ) ) 507 { 508 _returnToType = RETURN_TO_PREVIOUS_PAGE; 509 } 510 else if ( fwdPath.equals( RETURN_TO_CURRENT_PAGE_STR ) ) 511 { 512 _returnToType = RETURN_TO_CURRENT_PAGE; 513 } 514 else if ( fwdPath.equals( RETURN_TO_PAGE_LEGACY_STR ) ) 515 { 516 _returnToType = RETURN_TO_PAGE; } 518 else if ( fwdPath.equals( RETURN_TO_PREVIOUS_ACTION_STR ) ) 519 { 520 _returnToType = RETURN_TO_PREVIOUS_ACTION; 521 } 522 else if ( fwdPath.equals( RETURN_TO_ACTION_LEGACY_STR ) ) 523 { 524 _returnToType = RETURN_TO_ACTION; } 526 else 527 { 528 assert false : "invalid return-to type for forward " + fc.getName() + ": " + fwdPath; 529 _returnToType = RETURN_TO_CURRENT_PAGE; 530 } 531 } 532 533 String retFormMember = fc.getReturnFormMember(); 534 535 if ( retFormMember != null ) 536 { 537 try 538 { 539 assert _flowController != null; Field field = _flowController.getClass().getDeclaredField( retFormMember ); 541 returnFormClass = field.getType(); 542 field.setAccessible( true ); 543 ActionForm form = InternalUtils.wrapFormBean( field.get( _flowController ) ); 544 545 if ( form != null ) 546 { 547 if ( _log.isDebugEnabled() ) 548 { 549 _log.debug( "using member " + retFormMember + " for Forward " + getName() ); 550 } 551 552 addOutputForm( form ); 553 } 554 else 555 { 556 if ( _log.isInfoEnabled() ) 557 { 558 _log.info( "returnFormMember " + retFormMember + " was null." ); 559 } 560 } 561 } 562 catch ( NoSuchFieldException e ) 563 { 564 assert false : "could not find field " + retFormMember; } 566 catch ( IllegalAccessException e ) 567 { 568 assert false; } 570 } 571 572 if ( returnFormClass != null && _outputForms != null && _outputForms.size() > 0 ) 576 { 577 Object outputForm = InternalUtils.unwrapFormBean( ( ActionForm ) _outputForms.get( 0 ) ); 578 579 if ( ! returnFormClass.isInstance( outputForm ) ) 580 { 581 PageFlowException ex = 582 new IllegalOutputFormTypeException( getName(), _mappingPath, _flowController, 583 outputForm.getClass().getName(), 584 returnFormClass.getName() ); 585 InternalUtils.throwPageFlowException( ex ); 586 } 587 } 588 589 checkActionOutputs( fc ); 590 } 591 592 if ( ! _redirectSetThroughMethod ) setRedirect( fwd.getRedirect() ); 593 594 _init = true; 595 } 596 597 601 private void checkActionOutputs( PageFlowActionForward fc ) 602 { 603 PageFlowActionForward.ActionOutput[] actionOutputs = fc.getActionOutputs(); 604 boolean isInProductionMode = 605 AdapterManager.getServletContainerAdapter( _servletContext ).isInProductionMode(); 606 607 for ( int i = 0; i < actionOutputs.length; ++i ) 608 { 609 PageFlowActionForward.ActionOutput actionOutput = actionOutputs[i]; 610 611 if ( ! actionOutput.getNullable() 612 && ( _actionOutputs == null || _actionOutputs.get( actionOutput.getName() ) == null ) ) 613 { 614 PageFlowException ex = 615 new MissingActionOutputException( _mappingPath, _flowController, actionOutput.getName(), getName() ); 616 InternalUtils.throwPageFlowException( ex ); 617 } 618 619 if ( ! isInProductionMode && _actionOutputs != null ) 624 { 625 Object actualActionOutput = _actionOutputs.get( actionOutput.getName() ); 626 627 if ( actualActionOutput != null ) 628 { 629 String expectedTypeName = actionOutput.getType(); 630 int expectedArrayDims = 0; 631 632 while ( expectedTypeName.endsWith( "[]" ) ) 633 { 634 ++expectedArrayDims; 635 expectedTypeName = expectedTypeName.substring( 0, expectedTypeName.length() - 2 ); 636 } 637 638 Class expectedType = ( Class ) PRIMITIVE_TYPES.get( expectedTypeName ); 639 640 if ( expectedType == null ) 641 { 642 try 643 { 644 expectedType = Class.forName( expectedTypeName ); 645 } 646 catch ( ClassNotFoundException e ) 647 { 648 _log.error( "Could not load expected action output type " + expectedTypeName 649 + " for action output '" + actionOutput.getName() + "' on forward '" 650 + fc.getName() + "'; skipping type check." ); 651 continue; 652 } 653 } 654 655 Class actualType = actualActionOutput.getClass(); 656 int actualArrayDims = 0; 657 InternalStringBuilder arraySuffix = new InternalStringBuilder(); 658 659 while ( actualType.isArray() && actualArrayDims <= expectedArrayDims ) 660 { 661 ++actualArrayDims; 662 arraySuffix.append( "[]" ); 663 actualType = actualType.getComponentType(); 664 } 665 666 if ( actualArrayDims != expectedArrayDims || ! expectedType.isAssignableFrom( actualType ) ) 667 { 668 PageFlowException ex = 669 new MismatchedActionOutputException( _mappingPath, _flowController, 670 actionOutput.getName(), getName(), 671 expectedTypeName, 672 actualType.getName() + arraySuffix ); 673 InternalUtils.throwPageFlowException( ex ); 674 } 675 } 676 } 677 } 678 } 679 680 688 public void initialize( ActionMapping mapping, FlowController flowController ) 689 { 690 _mapping = mapping; 691 _mappingPath = mapping != null ? mapping.getPath() : null; 692 _flowController = flowController; 693 _servletContext = flowController.getServletContext(); 694 } 695 696 703 public void initialize( ActionMapping mapping, FlowController flowController, ServletRequest request ) 704 { 705 _mapping = mapping; 706 _mappingPath = mapping != null ? mapping.getPath() : null; 707 _flowController = flowController; 708 _servletContext = flowController.getServletContext(); 709 } 710 711 717 public void setPath( String contextRelativePath ) 718 { 719 path = contextRelativePath; 720 _init = true; 721 } 722 723 boolean isExplicitPath() 724 { 725 return _explicitPath; 726 } 727 728 734 public boolean hasExplicitRedirectValue() 735 { 736 return _redirectSetThroughMethod || _redirectSpecifiedOnAnnotation; 737 } 738 739 750 public String getPath() 751 { 752 init(); 753 return super.getPath(); 754 } 755 756 762 public boolean isNestedReturn() 763 { 764 init(); 765 return _isNestedReturn; 766 } 767 768 775 public boolean isReturnToPage() 776 { 777 init(); 778 return _returnToType == RETURN_TO_PREVIOUS_PAGE || _returnToType == RETURN_TO_CURRENT_PAGE 779 || _returnToType == RETURN_TO_PAGE; 780 } 781 782 790 public boolean isReturnToAction() 791 { 792 init(); 793 return _returnToType == RETURN_TO_PREVIOUS_ACTION || _returnToType == RETURN_TO_ACTION; 794 } 795 796 799 public boolean isExternalRedirect() 800 { 801 return _externalRedirect; 802 } 803 804 807 public void setExternalRedirect( boolean externalRedirect ) 808 { 809 _externalRedirect = externalRedirect; 810 if ( externalRedirect ) setRedirect( true ); 811 } 812 813 821 public boolean doesRestoreQueryString() 822 { 823 init(); 824 return _restoreQueryString; 825 } 826 827 833 public boolean forwardsToPageFlow() 834 { 835 return FileUtils.osSensitiveEndsWith( getPath(), PageFlowConstants.JPF_EXTENSION ); 836 } 837 838 845 public void setQueryString( String queryString ) 846 { 847 if ( queryString == null || queryString.length() == 0 ) 848 { 849 _queryString = null; 850 } 851 else if ( queryString.charAt( 0 ) == '?' ) 852 { 853 _queryString = new InternalStringBuilder( queryString ); 854 } 855 else 856 { 857 _queryString = new InternalStringBuilder( "?" ).append( queryString ); 858 } 859 } 860 861 867 public String getQueryString() 868 { 869 return _queryString != null ? _queryString.toString() : null; 870 } 871 872 878 public void addQueryParam( String paramName, String value ) 879 { 880 if ( _queryString == null ) 881 { 882 _queryString = new InternalStringBuilder( "?" ); 883 } 884 else 885 { 886 _queryString.append( '&' ); 887 } 888 889 _queryString.append( paramName ); 890 891 if ( value != null ) 892 { 893 _queryString.append( '=' ).append( value ); 894 } 895 } 896 897 902 public final void addQueryParam( String paramName ) 903 { 904 addQueryParam( paramName, null ); 905 } 906 907 914 public void addPageInput( String paramName, Object value ) 915 { 916 addActionOutput( paramName, value ); 917 } 918 919 925 public void addActionOutput( String paramName, Object value ) 926 { 927 if ( paramName == null || paramName.length() == 0 ) 928 { 929 throw new IllegalArgumentException ( "An action output name may not be null or empty." ); 930 } 931 932 if ( _actionOutputs == null ) 933 { 934 _actionOutputs = new HashMap (); 935 } 936 937 if ( _init && getRedirect() ) 942 { 943 String actionPath = _mappingPath != null ? _mappingPath : ""; 944 String descrip = getName() != null ? getName() : getPath(); 945 PageFlowException ex = new IllegalActionOutputException( descrip, actionPath, _flowController, paramName ); 946 InternalUtils.throwPageFlowException( ex ); 947 } 948 949 _actionOutputs.put( paramName, value ); 950 } 951 952 959 public Map getPageInputs() 960 { 961 return getActionOutputs(); 962 } 963 964 970 public Map getActionOutputs() 971 { 972 return _actionOutputs; 973 } 974 975 984 public int getReturnToType() 985 { 986 return _returnToType; 987 } 988 989 998 public String getReturnToTypeAsString() 999 { 1000 switch ( _returnToType ) 1001 { 1002 case RETURN_TO_CURRENT_PAGE: 1003 return RETURN_TO_CURRENT_PAGE_STR; 1004 1005 case RETURN_TO_PREVIOUS_PAGE: 1006 return RETURN_TO_PREVIOUS_PAGE_STR; 1007 1008 case RETURN_TO_PAGE: 1009 return RETURN_TO_PAGE_LEGACY_STR; 1010 1011 case RETURN_TO_PREVIOUS_ACTION: 1012 return RETURN_TO_PREVIOUS_ACTION_STR; 1013 1014 case RETURN_TO_ACTION: 1015 return RETURN_TO_ACTION_LEGACY_STR; 1016 } 1017 1018 return null; 1019 } 1020 1021 void reinitialize( FlowController fc ) 1022 { 1023 _flowController = fc; 1024 _servletContext = fc.getServletContext(); 1025 1026 if ( _mapping == null && _mappingPath != null ) 1027 { 1028 ModuleConfig mc = fc.getModuleConfig(); 1029 assert mc != null : "no ModuleConfig found for " + fc.getClass().getName(); 1030 _mapping = ( ActionMapping ) mc.findActionConfig( _mappingPath ); 1031 } 1032 } 1033} 1034 | Popular Tags |