You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by mr...@apache.org on 2005/08/26 07:46:58 UTC
svn commit: r240168 [7/30] - in /struts/sandbox/trunk/ti: ./
core/src/java/org/apache/ti/ core/src/java/org/apache/ti/config/
core/src/java/org/apache/ti/config/mapper/
core/src/java/org/apache/ti/core/ core/src/java/org/apache/ti/core/factory/
core/sr...
Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/PageFlowRequestProcessor.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/PageFlowRequestProcessor.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/PageFlowRequestProcessor.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/PageFlowRequestProcessor.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,898 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import org.apache.ti.pageflow.handler.Handlers;
+import org.apache.ti.pageflow.internal.InternalConstants;
+import org.apache.ti.util.logging.Logger;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * The Page Flow extension of the Struts RequestProcessor, which contains callbacks that are invoked
+ * during processing of a request to the Struts action servlet. This class is registered as the
+ * <strong>controller</strong> for all Struts modules derived from page flows.
+ */
+public class PageFlowRequestProcessor
+ implements Serializable, InternalConstants, PageFlowConstants {
+
+ private static int requestNumber = 0;
+
+ private static final Logger _log = Logger.getInstance(PageFlowRequestProcessor.class);
+
+ private static final String ACTION_OVERRIDE_PARAM_PREFIX = "actionOverride:";
+ private static final int ACTION_OVERRIDE_PARAM_PREFIX_LEN = ACTION_OVERRIDE_PARAM_PREFIX.length();
+ private static final String SCHEME_UNSECURE = "http";
+ private static final String SCHEME_SECURE = "https";
+ private static final String REDIRECT_REQUEST_ATTRS_PREFIX = InternalConstants.ATTR_PREFIX + "requestAttrs:";
+ private static final String REDIRECT_REQUEST_ATTRS_PARAM = "forceRedirect";
+
+
+ private Map/*< String, Class >*/ _formBeanClasses = new HashMap/*< String, Class >*/();
+ // private Map/*< String, List< PageFlowAction > >*/ _overloadedActions = new HashMap/*< String, List< PageFlowAction > >*/();
+ private ContainerAdapter _containerAdapter;
+ private Handlers _handlers;
+ private FlowControllerFactory _flowControllerFactory;
+ //private InternalConcurrentHashMap/*< String, Class >*/ _pageServletClasses = new InternalConcurrentHashMap/*< String, Class >*/();
+ //private PageFlowPageFilter _pageServletFilter;
+
+ /*
+ protected void processPopulate( HttpServletRequest request, HttpServletResponse response, Object form,
+ PageFlowAction mapping )
+ throws PageFlowException
+ {
+ //
+ // If a previous action forwarded us a form, use that -- don't populate it from request parameters.
+ //
+ Object previousForm = InternalUtils.getForwardedFormBean( request, true );
+
+ if ( previousForm != null )
+ {
+ return;
+ }
+
+ if ( _log.isDebugEnabled() )
+ {
+ _log.debug( "Populating bean properties from this request" );
+ }
+
+ // struts does this
+ if ( form != null )
+ {
+ form.setServlet( servlet );
+ form.reset( mapping, request );
+ }
+
+ if ( mapping.getMultipartClass() != null )
+ {
+ request.setAttribute( Globals.MULTIPART_KEY, mapping.getMultipartClass() );
+ }
+
+ PageFlowActionContext requestWrapper = getContext();
+ boolean alreadyCalledInRequest = requestWrapper.isProcessPopulateAlreadyCalled();
+ if ( ! alreadyCalledInRequest ) requestWrapper.setProcessPopulateAlreadyCalled( true );
+
+ //
+ // If this is a forwarded request and the form-bean is null, don't call to ProcessPopulate.
+ // We don't want to expose errors due to parameters from the original request, which won't
+ // apply to a forwarded action that doesn't take a form.
+ //
+ if ( !alreadyCalledInRequest || form != null )
+ {
+ //
+ // If this request was forwarded by a button-override of the main form action, then ensure that there are
+ // no databinding errors when the override action does not use a form bean.
+ //
+ if ( form == null && requestWrapper.isForwardedByButton() ) form = NULL_ACTION_FORM;
+
+ ProcessPopulate.populate( request, response, form, alreadyCalledInRequest );
+ }
+ }
+ */
+
+ /**
+ * The requested action can be overridden by a request parameter. In this case, we parse the action from
+ * the request parameter and forward to a URI constructed from it.
+ *
+ * @return <code>true</code> if the action was overridden by a request parameter, in which case the request
+ * was forwarded.
+ * @throws IOException
+ * @throws PageFlowException
+ */
+ /* TODO: re-add this -- it should be in Chain
+ protected boolean processActionOverride()
+ throws IOException, PageFlowException
+ {
+ // Only make this check if this is an initial (non-forwarded) request.
+ //
+ // TODO: performance?
+ //
+ PageFlowActionContext wrapper = getContext();
+ if ( ! wrapper.isForwardedByButton() && ! wrapper.isForwardedRequest() )
+ {
+ //
+ // First, since we need access to request parameters here, process a multipart request
+ // if that's what we have. This puts the parameters (each in a MIME part) behind an
+ // interface that makes them look like normal request parameters.
+ //
+ // TODO: re-add multipart support
+ // HttpServletRequest multipartAwareRequest = processMultipart( request );
+
+
+ for ( Iterator i = getContext().getWebContext().getRequestScope().keySet().iterator(); i.hasNext(); )
+ {
+ String paramName = ( String ) i.next();
+
+ if ( paramName.startsWith( ACTION_OVERRIDE_PARAM_PREFIX ) )
+ {
+ String actionPath = paramName.substring( ACTION_OVERRIDE_PARAM_PREFIX_LEN );
+
+ String qualifiedAction = InternalUtils.qualifyAction( actionPath );
+ actionPath = InternalUtils.createActionPath(qualifiedAction );
+
+ if ( _log.isDebugEnabled() )
+ {
+ _log.debug( "A request parameter overrode the action. Forwarding to: " + actionPath );
+ }
+
+ wrapper.setForwardedByButton( true );
+ doForward( actionPath );
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private void processSecurity()
+ {
+ PageFlowActionContext context = PageFlowActionContext.getContext();
+ Map requestScope = context.getWebContext().getRequestScope();
+ String uri = context.getRequestPath();
+
+ //
+ // Allow the container to do a security check on forwarded requests, if that feature is enabled.
+ //
+ PageflowConfig pageflowConfig = ConfigUtil.getConfig().getPageflowConfig();
+ if ( pageflowConfig != null && pageflowConfig.getEnsureSecureForwards() && getContext().isForwardedRequest() )
+ {
+ //
+ // In some situations (namely, in scoped requests under portal), the initial
+ // security check may not have been done for the request URI. In this case, a redirect
+ // to https may happen during checkSecurity().
+ //
+ if ( _containerAdapter.doSecurityRedirect( uri) )
+ {
+ if ( _log.isDebugEnabled() )
+ {
+ _log.debug( "checkSecurity() caused a redirect. Ending processing for this request "
+ + '(' + uri + ')' );
+ }
+
+ return;
+ }
+ }
+
+ //
+ // If we've come in on a forced redirect due to security constraints, look for request attrs
+ // that we put into the session.
+ //
+ String hash = ( String ) requestScope.get( REDIRECT_REQUEST_ATTRS_PARAM );
+ if ( hash != null )
+ {
+ Map sessionScope = getContext().getSession();
+
+ if ( sessionScope != null )
+ {
+ String carryoverAttrName = makeRedirectedRequestAttrsKey( uri, hash );
+ Map attrs = ( Map ) sessionScope.get( carryoverAttrName );
+ sessionScope.remove( carryoverAttrName );
+
+ if ( attrs != null )
+ {
+ for ( Iterator i = attrs.entrySet().iterator(); i.hasNext(); )
+ {
+ Map.Entry entry = ( Map.Entry ) i.next();
+
+ String attrName = ( String ) entry.getKey();
+ if ( requestScope.get( attrName ) == null )
+ {
+ requestScope.put( attrName, entry.getValue() );
+ }
+ }
+ }
+ }
+ }
+ }
+ */
+
+ /**
+ * Process any direct request for a page flow by forwarding to its "begin" action.
+ *
+ * @param request the current HttpServletRequest
+ * @param response the current HttpServletResponse
+ * @param uri the decoded request URI
+ * @return <code>true</code> if the request was for a page flow, in which case it was forwarded.
+ * @throws IOException
+ * @throws PageFlowException
+ */
+ /* TODO: re-add
+ protected boolean processPageFlowRequest( HttpServletRequest request, HttpServletResponse response, String uri )
+ throws IOException, PageFlowException
+ {
+ //
+ // forward requests for *.jpf to the "begin" action within the appropriate Struts module.
+ //
+ if ( FileUtils.osSensitiveEndsWith( uri, PageFlowConstants.PAGEFLOW_EXTENSION ) )
+ {
+ //
+ // Make sure the current module config matches the request URI. If not, this could be an
+ // EAR where the jpf-struts-config.xml wasn't included because of a compilation error.
+ //
+ String modulePath = PageFlowUtils.getModulePath( request );
+ if ( ! moduleConfig.getPrefix().equals( modulePath ) )
+ {
+ if ( _log.isErrorEnabled() )
+ {
+ InternalStringBuilder msg = new InternalStringBuilder( "No module configuration registered for " );
+ msg.append( uri ).append( " (namespace " ).append( namespace ).append( ")." );
+ _log.error( msg.toString() );
+ }
+
+ if ( modulePath.length() == 0 ) modulePath = "/";
+ InternalUtils.sendDevTimeError( "PageFlow_NoModuleConf", null,
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR, request, response,
+ getServletContext(), new Object[]{ uri, modulePath } );
+ return true;
+ }
+
+ //
+ // Make sure that the requested pageflow matches the pageflow for the directory.
+ //
+ PageFlowAction beginMapping = getBeginMapping();
+ if ( beginMapping != null )
+ {
+ String desiredType = beginMapping.getParameter();
+ desiredType = desiredType.substring( desiredType.lastIndexOf( '.' ) + 1 ) + JPF_EXTENSION;
+ String requestedType = InternalUtils.getRequestPath( request );
+ requestedType = requestedType.substring( requestedType.lastIndexOf( '/' ) + 1 );
+
+ if ( ! requestedType.equals( desiredType ) )
+ {
+ if ( _log.isDebugEnabled() )
+ {
+ _log.debug( "Wrong .jpf requested for this directory: got " + requestedType
+ + ", expected " + desiredType );
+ }
+
+ if ( _log.isErrorEnabled() )
+ {
+ InternalStringBuilder msg = new InternalStringBuilder( "Wrong .jpf requested for this directory: got " );
+ msg.append( requestedType ).append( ", expected " ).append( desiredType ).append( '.' );
+ _log.error( msg.toString() );
+ }
+
+ InternalUtils.sendDevTimeError( "PageFlow_WrongPath", null,
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR, request, response,
+ getServletContext(), new Object[]{ requestedType, desiredType } );
+
+ return true;
+ }
+ }
+
+ uri = PageFlowUtils.getBeginActionURI( uri );
+
+ if ( _log.isDebugEnabled() )
+ {
+ _log.debug( "Got request for " + request.getRequestURI() + ", forwarding to " + uri );
+ }
+
+ doForward( uri, request, response );
+ return true;
+ }
+
+ return false;
+ }
+
+ protected PageFlowAction getBeginMapping()
+ {
+ return ( PageFlowAction ) moduleConfig.findActionConfig( BEGIN_ACTION_PATH );
+ }
+
+ */
+
+ /**
+ * A MultipartRequestWrapper that we cache in the outer request once we've handled the multipart request once.
+ * It extends the base Struts MultipartRequestWrapper by being aware of ScopedRequests; for ScopedRequests, it
+ * filters the parameter names accordingly.
+ */
+ /* TODO: re-add
+ private static class RehydratedMultipartRequestWrapper extends MultipartRequestWrapper
+ {
+ public RehydratedMultipartRequestWrapper( HttpServletRequest req )
+ {
+ super( req );
+
+ MultipartRequestHandler handler = MultipartRequestUtils.getCachedMultipartHandler( req );
+
+ if ( handler != null )
+ {
+ ScopedRequest scopedRequest = ScopedUtils.unwrapRequest( req );
+ Map textElements = handler.getTextElements();
+ parameters = scopedRequest != null ? scopedRequest.filterParameterMap( textElements ) : textElements;
+ }
+ }
+ }
+ */
+
+ /**
+ * If this is a multipart request, wrap it with a special wrapper. Otherwise, return the request unchanged.
+ */
+ /* TODO: re-add multipart support
+ protected HttpServletRequest processMultipart( HttpServletRequest request )
+ {
+ if ( ! "POST".equalsIgnoreCase( request.getMethod() ) ) return request;
+
+ String contentType = request.getContentType();
+ if ( contentType != null && contentType.startsWith( "multipart/form-data" ) )
+ {
+ PageFlowActionContext pageFlowRequestWrapper = getContext();
+
+ //
+ // We may have already gotten a multipart wrapper during process(). If so, use that.
+ //
+ MultipartRequestWrapper cachedWrapper = pageFlowRequestWrapper.getMultipartRequestWrapper();
+
+ if ( cachedWrapper != null && cachedWrapper.getRequest() == request ) return cachedWrapper;
+
+ try
+ {
+ //
+ // First, pre-handle the multipart request. This parses the stream and caches a single
+ // MultipartRequestHandler in the outer request, so we can create new wrappers around it at will.
+ //
+ MultipartRequestUtils.preHandleMultipartRequest( request );
+ }
+ catch ( PageFlowException e )
+ {
+ _log.error( "Could not parse multipart request.", e.getRootCause() );
+ return request;
+ }
+
+ MultipartRequestWrapper ret = new RehydratedMultipartRequestWrapper( request );
+ pageFlowRequestWrapper.setMultipartRequestWrapper( ret );
+ return ret;
+ }
+ else
+ {
+ return request;
+ }
+
+ }
+ */
+
+ /*
+ private boolean isCorrectFormType( Class formBeanClass, PageFlowAction mapping )
+ {
+ assert mapping.getName() != null : "cannot pass an PageFlowAction that has no form bean";
+ Class cachedFormBeanClass = ( Class ) _formBeanClasses.get( mapping.getName() );
+ return isCorrectFormType( formBeanClass, cachedFormBeanClass, mapping );
+ }
+
+ private boolean isCorrectFormType( Class formBeanClass, Class actionMappingFormBeanClass, PageFlowAction mapping )
+ {
+ if ( actionMappingFormBeanClass != null )
+ {
+ return actionMappingFormBeanClass .isAssignableFrom( formBeanClass );
+ }
+ else
+ {
+ //
+ // The form bean class couldn't be loaded at init time -- just check against the class name.
+ //
+ FormBeanConfig mappingFormBean = moduleConfig.findFormBeanConfig( mapping.getName() );
+ String formClassName = formBeanClass.getName();
+
+ if ( mappingFormBean != null && mappingFormBean.getType().equals( formClassName ) ) return true;
+
+ if ( mapping instanceof PageFlowAction )
+ {
+ String desiredType = ( ( PageFlowAction ) mapping ).getFormBeanClass();
+ if ( formClassName.equals( desiredType ) ) return true;
+ }
+ }
+
+ return false;
+ }
+ */
+
+ /* TODO: re-add tokens, or something like it.
+ private PageFlowAction checkTransaction( HttpServletRequest request, HttpServletResponse response,
+ PageFlowAction mapping, String actionPath )
+ throws IOException
+ {
+ if ( mapping instanceof PageFlowAction && ( ( PageFlowAction ) mapping ).isPreventDoubleSubmit() )
+ {
+ if ( ! TokenProcessor.getInstance().isTokenValid( request, true ) )
+ {
+ FlowController currentFC = getContext().getCurrentFlowController();
+ String actionName = InternalUtils.getActionName( mapping );
+ DoubleSubmitException ex = new DoubleSubmitException( actionName, currentFC );
+
+ if ( currentFC != null )
+ {
+ try
+ {
+ forward fwd = currentFC.handleException( ex, mapping, null, request, response );
+ return new ExceptionHandledPageFlowAction( actionPath, fwd );
+ }
+ catch ( PageFlowException servletException)
+ {
+ _log.error( "Exception occurred while handling " + ex.getClass().getName(), servletException );
+ }
+ }
+
+ ex.sendResponseErrorCode( response );
+ return null;
+ }
+ }
+
+ return mapping;
+ }
+ */
+ public void init()
+ throws PageFlowException {
+ //
+ // Cache a list of overloaded actions for each overloaded action path (actions are overloaded by form bean type).
+ //
+ // TODO: re-add overloaded action support
+// cacheOverloadedPageFlowActions();
+
+ //
+ // Cache the form bean Classes by form bean name.
+ //
+ // TODO: re-add class caching?
+// cacheFormClasses();
+ }
+
+ /* TODO: re-add overloaded action support
+ private void cacheOverloadedPageFlowActions()
+ {
+ ActionConfig[] actionConfigs = moduleConfig.findActionConfigs();
+
+ for ( int i = 0; i < actionConfigs.length; i++ )
+ {
+ ActionConfig actionConfig = actionConfigs[i];
+
+ if ( actionConfig instanceof PageFlowAction )
+ {
+ PageFlowAction mapping = ( PageFlowAction ) actionConfig;
+ String unqualifiedActionPath = ( ( PageFlowAction ) actionConfig ).getUnqualifiedActionPath();
+
+ if ( unqualifiedActionPath != null )
+ {
+ List overloaded = ( List ) _overloadedActions.get( unqualifiedActionPath );
+
+ if ( overloaded == null )
+ {
+ overloaded = new ArrayList();
+ _overloadedActions.put( unqualifiedActionPath, overloaded );
+ }
+
+ overloaded.add( mapping );
+ }
+ }
+ }
+ }
+ */
+
+ /*
+ private void cacheFormClasses()
+ {
+ FormBeanConfig[] formBeans = moduleConfig.findFormBeanConfigs();
+ ReloadableClassHandler rch = _handlers.getReloadableClassHandler();
+
+ for ( int i = 0; i < formBeans.length; i++ )
+ {
+ FormBeanConfig formBeanConfig = formBeans[i];
+ String formType = InternalUtils.getFormBeanType( formBeanConfig );
+
+ try
+ {
+ Class formBeanClass = rch.loadClass( formType );
+ _formBeanClasses.put( formBeanConfig.getName(), formBeanClass );
+ }
+ catch ( ClassNotFoundException e )
+ {
+ _log.error( "Could not load class " + formType + " referenced from form bean config "
+ + formBeanConfig.getName() + " in Struts module " + moduleConfig );
+ }
+ }
+ }
+ */
+
+ /**
+ * Read component instance mapping configuration file.
+ * This is where we read files properties.
+ */
+
+ /* TODO: re-add Tiles support
+ protected void initDefinitionsMapping() throws PageFlowException
+ {
+ definitionsFactory = null;
+ TilesUtilImpl tilesUtil = TilesUtil.getTilesUtil();
+
+ if ( tilesUtil instanceof TilesUtilStrutsImpl )
+ {
+ // Retrieve and set factory for this modules
+ definitionsFactory =
+ ( ( TilesUtilStrutsImpl ) tilesUtil ).getDefinitionsFactory( getServletContext(), moduleConfig );
+
+ if ( definitionsFactory == null && log.isDebugEnabled() )
+ {
+ log.debug( "Definition Factory not found for module: '"
+ + moduleConfig.getPrefix() );
+ }
+ }
+ }
+ */
+
+ /* TODO: re-add this. It's code to customize XWork -- to handle shared flow and form-bean-specific actions.
+ public PageFlowAction processMapping( HttpServletRequest request, HttpServletResponse response, String path )
+ throws IOException
+ {
+ FlowController fc = getContext().getFlowController();
+ Object forwardedForm = InternalUtils.getForwardedFormBean( false );
+
+ //
+ // First, see if this is a request for a shared flow action. The shared flow's name (as declared by the
+ // current page flow) will precede the dot.
+ //
+ if ( fc != null && ! processSharedFlowMapping( request, response, path, fc ) ) return null;
+
+ //
+ // Look for a form-specific action path. This is used when there are two actions with the same
+ // name, but different forms (in nesting).
+ //
+ Class forwardedFormClass = null;
+
+ if ( forwardedForm != null )
+ {
+ forwardedFormClass = forwardedForm.getClass();
+ List possibleMatches = ( List ) _overloadedActions.get( path );
+ PageFlowAction bestMatch = null;
+
+ //
+ // Troll through the overloaded actions for the given path. Look for the one whose form bean class is
+ // exactly the class of the forwarded form; failing that, look for one that's assignable from the class
+ // of the forwarded form.
+ //
+ for ( int i = 0; possibleMatches != null && i < possibleMatches.size(); ++i )
+ {
+ PageFlowAction possibleMatch = ( PageFlowAction ) possibleMatches.get( i );
+ Class cachedFormBeanClass = ( Class ) _formBeanClasses.get( possibleMatch.getName() );
+
+ if ( forwardedFormClass.equals( cachedFormBeanClass ) )
+ {
+ bestMatch = possibleMatch;
+ break;
+ }
+ if ( bestMatch == null && isCorrectFormType( forwardedFormClass, possibleMatch ) )
+ {
+ bestMatch = possibleMatch;
+ }
+ }
+
+ if ( bestMatch != null )
+ {
+ request.setAttribute( Globals.MAPPING_KEY, bestMatch );
+
+ if ( _log.isDebugEnabled() )
+ {
+ _log.debug( "Found form-specific action mapping " + bestMatch.getPath() + " for " + path
+ + ", form " + forwardedFormClass.getName() );
+ }
+
+ return checkTransaction(bestMatch);
+ }
+ }
+
+ //
+ // Look for a directly-defined mapping for this path.
+ //
+ PageFlowAction mapping = ( PageFlowAction ) moduleConfig.findActionConfig( path );
+
+ if ( mapping != null )
+ {
+ boolean wrongForm = false;
+
+ //
+ // We're going to bail out if there is a forwarded form and this mapping requires a different form type.
+ //
+ if ( forwardedForm != null )
+ {
+ boolean mappingHasNoFormBean = mapping.getName() == null;
+ wrongForm = mappingHasNoFormBean || ! isCorrectFormType( forwardedFormClass, mapping );
+ }
+
+ if ( ! wrongForm )
+ {
+ request.setAttribute( Globals.MAPPING_KEY, mapping );
+ return checkTransaction( request, response, mapping, path );
+ }
+ }
+
+ //
+ // getContext().getOriginalServletPath returns the request URI we had before trying to forward to an action
+ // in a shared flow.
+ //
+ String errorServletPath = getContext().getOriginalServletPath();
+
+ //
+ // If the error action path has a slash in it, then it's not local to the current page flow. Replace
+ // it with the original servlet path.
+ //
+ if ( errorServletPath != null && path.indexOf( '/' ) > 0 ) path = errorServletPath;
+ return processUnresolvedAction( path, request, response, forwardedForm );
+ }
+
+ protected boolean processSharedFlowMapping( HttpServletRequest request, HttpServletResponse response,
+ String actionPath, FlowController currentFlowController )
+ throws IOException
+ {
+ if ( currentFlowController.isPageFlow() )
+ {
+ int dot = actionPath.indexOf( '.' );
+
+ if ( dot != -1 )
+ {
+ Map/*< String, SharedFlowController >* sharedFlows = PageFlowUtils.getSharedFlows( request );
+ if ( sharedFlows == null ) return true;
+ if ( dot == actionPath.length() - 1 ) return true; // empty action name
+ assert actionPath.length() > 0 && actionPath.charAt( 0 ) == '/' : actionPath;
+ String sharedFlowName = actionPath.substring( 1, dot );
+ SharedFlowController sf = ( SharedFlowController ) sharedFlows.get( sharedFlowName );
+
+ if ( sf != null )
+ {
+ if ( _log.isDebugEnabled() )
+ {
+ _log.debug( "Forwarding to shared flow " + sf.getDisplayName() + " to handle action \""
+ + actionPath + "\"." );
+ }
+
+ //
+ // Save the original request URI, so if the action fails on the shared flow, too, then we can
+ // give an error message that includes *this* URI, not the shared flow URI.
+ //
+ getContext().setOriginalServletPath( InternalUtils.getRequestPath( request ) );
+
+ //
+ // Construct a URI that is [shared flow namespace] + [base action path] + [action-extension (.do)]
+ //
+ int lastSlash = actionPath.lastIndexOf( '/' );
+ assert lastSlash != -1 : actionPath;
+ InternalStringBuilder uri = new InternalStringBuilder( sf.getModulePath() );
+ uri.append( '/' );
+ uri.append( actionPath.substring( dot + 1 ) );
+ uri.append( ACTION_EXTENSION );
+
+ try
+ {
+ doForward( uri.toString(), request, response );
+ return false;
+ }
+ catch ( PageFlowException e )
+ {
+ _log.error( "Could not forward to shared flow URI " + uri, e );
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ protected PageFlowAction processUnresolvedAction( String actionPath, HttpServletRequest request,
+ HttpServletResponse response, Object returningForm )
+ throws IOException
+ {
+ if ( _log.isInfoEnabled() )
+ {
+ InternalStringBuilder msg = new InternalStringBuilder( "action \"" ).append( actionPath );
+ _log.info( msg.append( "\" was also unhandled by Global.app." ).toString() );
+ }
+
+ //
+ // If there's a PageFlowController for this request, try and let it handle an
+ // action-not-found exception. Otherwise, let Struts print out its "invalid path"
+ // message.
+ //
+ FlowController fc = PageFlowUtils.getCurrentPageFlow( request );
+
+ try
+ {
+ if ( fc != null )
+ {
+ Exception ex = new ActionNotFoundException( actionPath, fc, returningForm );
+ InternalUtils.setCurrentModule( fc.getModuleConfig(), request );
+ forward result = fc.handleException( ex, null, null, request, response );
+ return new ExceptionHandledPageFlowAction( actionPath, result );
+ }
+ }
+ catch ( PageFlowException e )
+ {
+ // ignore this -- just let Struts do its thing.
+
+ if ( _log.isDebugEnabled() )
+ {
+ _log.debug( e );
+ }
+ }
+
+ if ( _log.isDebugEnabled() )
+ {
+ _log.debug( "Couldn't handle an ActionNotFoundException -- delegating to Struts" );
+ }
+
+ return super.processMapping( request, response, actionPath );
+ }
+ */
+
+ /*
+ protected boolean processRoles( HttpServletRequest request, HttpServletResponse response, PageFlowAction mapping )
+ throws IOException, PageFlowException
+ {
+ //
+ // If there are no required roles for this action, just return.
+ //
+ String roles[] = mapping.getRoleNames();
+ if ( roles == null || roles.length < 1 )
+ {
+ return true;
+ }
+
+ // Check the current user against the list of required roles
+ FlowController fc = getContext().getCurrentFlowController();
+ FlowControllerHandlerContext context = new FlowControllerHandlerContext( request, response, fc );
+
+ for ( int i = 0; i < roles.length; i++ )
+ {
+ if ( _handlers.getLoginHandler().isUserInRole( context, roles[i] ) )
+ {
+ if ( _log.isDebugEnabled() )
+ {
+ _log.debug( " User " + request.getRemoteUser() + " has role '" + roles[i] + "', granting access" );
+ }
+
+ return true;
+ }
+ }
+
+ // The current user is not authorized for this action
+ if ( _log.isDebugEnabled() )
+ {
+ _log.debug( " User '" + request.getRemoteUser() + "' does not have any required role, denying access" );
+ }
+
+ //
+ // Here, Struts sends an HTTP error. We try to let the current page flow handle a relevant exception.
+ //
+ LoginHandler loginHandler = _handlers.getLoginHandler();
+ String actionName = InternalUtils.getActionName( mapping );
+ FlowController currentFC = getContext().getCurrentFlowController();
+ FlowControllerException ex;
+
+ if ( loginHandler.getUserPrincipal( context ) == null )
+ {
+ ex = currentFC.createNotLoggedInException( actionName, request );
+ }
+ else
+ {
+ ex = new UnfulfilledRolesException( mapping.getRoleNames(), mapping.getRoles(), actionName, currentFC );
+ }
+
+ if ( currentFC != null )
+ {
+ forward fwd = currentFC.handleException( ex, mapping, null, request, response );
+ processForwardConfig( request, response, fwd );
+ }
+ else
+ {
+ ( ( ResponseErrorCodeSender ) ex ).sendResponseErrorCode( response );
+ }
+
+ return false;
+ }
+
+ private static String addScopeParams( String url, HttpServletRequest request )
+ {
+ //
+ // If the current request is scoped, add the right request parameter to the URL.
+ //
+ String scopeID = request.getParameter( ScopedUtils.SCOPE_ID_PARAM );
+ if ( scopeID != null )
+ {
+ return InternalUtils.addParam( url, ScopedUtils.SCOPE_ID_PARAM, scopeID );
+ }
+ else
+ {
+ return url;
+ }
+ }
+
+ */
+
+
+ /**
+ * Set the no-cache headers. This overrides the base Struts behavior to prevent caching even for the pages.
+ */
+ /*
+ protected void processNoCache( HttpServletRequest request, HttpServletResponse response )
+ {
+ //
+ // Set the no-cache headers if:
+ // 1) the module is configured for it, or
+ // 2) netui-config.xml has an "always" value for <pageflow-config><prevent-cache>, or
+ // 3) netui-config.xml has an "inDevMode" value for <pageflow-config><prevent-cache>, and we're not in
+ // production mode.
+ //
+ boolean noCache = moduleConfig.getControllerConfig().getNocache();
+
+ if ( ! noCache )
+ {
+ PageflowConfig pfConfig = ConfigUtil.getConfig().getPageflowConfig();
+
+ if ( pfConfig != null )
+ {
+ PageflowConfig.PreventCache.Enum preventCache = pfConfig.getPreventCache();
+
+ if ( preventCache != null )
+ {
+ switch ( preventCache.intValue() )
+ {
+ case PageflowConfig.PreventCache.INT_ALWAYS:
+ noCache = true;
+ break;
+ case PageflowConfig.PreventCache.INT_IN_DEV_MODE:
+ noCache = ! _containerAdapter.isInProductionMode();
+ break;
+ }
+ }
+ }
+ }
+
+ if ( noCache )
+ {
+ //
+ // The call to PageFlowPageFilter.preventCache() will cause caching to be prevented
+ // even when we end up forwarding to a pagee. Normally, no-cache headers are lost
+ // when a server forward occurs.
+ //
+ ServletUtils.preventCache( response );
+ PageFlowPageFilter.preventCache( request );
+ }
+ }
+ */
+
+}
Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/PageFlowStack.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/PageFlowStack.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/PageFlowStack.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/PageFlowStack.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import org.apache.ti.pageflow.handler.Handlers;
+import org.apache.ti.pageflow.handler.StorageHandler;
+import org.apache.ti.pageflow.interceptor.action.ActionInterceptor;
+import org.apache.ti.pageflow.interceptor.action.ActionInterceptorContext;
+import org.apache.ti.pageflow.interceptor.action.InterceptorForward;
+import org.apache.ti.pageflow.internal.InternalConstants;
+import org.apache.ti.pageflow.internal.InternalUtils;
+import org.apache.ti.util.logging.Logger;
+
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import java.io.Serializable;
+import java.util.Stack;
+
+
+/**
+ * Stack for keeping track of a series of nested page flows. When a nested page flow is entered,
+ * the previous page flow is pushed onto this stack, which is kept in the user session.
+ */
+public class PageFlowStack
+ implements HttpSessionBindingListener, Serializable {
+
+ private static final Logger _log = Logger.getInstance(PageFlowStack.class);
+ private static final String JPF_STACK_ATTR = InternalConstants.ATTR_PREFIX + "nestingStack";
+
+ private Stack _stack = new Stack();
+
+
+ /**
+ * Wrapper that contains a pushed page flow and information related to it.
+ */
+ public static class PushedPageFlow implements Serializable {
+
+ private PageFlowController _pageFlow;
+ private ActionInterceptor _interceptor;
+ private InterceptorForward _interceptedForward;
+ private String _interceptedActionName;
+
+ public PushedPageFlow(PageFlowController pageFlow) {
+ _pageFlow = pageFlow;
+ }
+
+ public PushedPageFlow(PageFlowController pageFlow, ActionInterceptor interceptor,
+ InterceptorForward interceptedFwd, String interceptedActionName) {
+ this(pageFlow);
+ _interceptor = interceptor;
+ _interceptedForward = interceptedFwd;
+ _interceptedActionName = interceptedActionName;
+ }
+
+ public PageFlowController getPageFlow() {
+ return _pageFlow;
+ }
+
+ public ActionInterceptor getInterceptor() {
+ return _interceptor;
+ }
+
+ public InterceptorForward getInterceptedForward() {
+ return _interceptedForward;
+ }
+
+ public String getInterceptedActionName() {
+ return _interceptedActionName;
+ }
+ }
+
+ /**
+ * Get the stack of nested page flows for the current user session. Create and store an empty
+ * stack if none exists.
+ *
+ * @return the stack of nested page flows {@link PushedPageFlow}s) for the current user session.
+ */
+ public static PageFlowStack get() {
+ return get(true);
+ }
+
+ /**
+ * Get the stack of nested page flows for the current user session. Create and store an empty
+ * stack if none exists.
+ *
+ * @return a {@link PageFlowStack} of nested page flows ({@link PageFlowController}s) for the current user session.
+ */
+ public static PageFlowStack get(boolean createIfNotExist) {
+ StorageHandler sh = Handlers.get().getStorageHandler();
+ String attrName = InternalUtils.getScopedAttrName(JPF_STACK_ATTR);
+ PageFlowStack jpfStack = (PageFlowStack) sh.getAttribute(attrName);
+
+ if (jpfStack == null && createIfNotExist) {
+ jpfStack = new PageFlowStack();
+ jpfStack.save();
+ }
+
+ return jpfStack;
+ }
+
+ /**
+ * Destroy the stack of {@link PageFlowController}s that have invoked nested page flows.
+ */
+ public void destroy() {
+ StorageHandler sh = Handlers.get().getStorageHandler();
+ String attrName = InternalUtils.getScopedAttrName(JPF_STACK_ATTR);
+
+ sh.removeAttribute(attrName);
+ }
+
+ /**
+ * Pop page flows from the nesting stack until one of the given type is found.
+ *
+ * @return the last popped page flow if one of the given type was found, or <code>null</code>
+ * if none was found.
+ */
+ PageFlowController popUntil(Class stopAt) {
+ while (!isEmpty()) {
+ PageFlowController popped = pop().getPageFlow();
+
+ if (popped.getClass().equals(stopAt)) {
+ //
+ // If we've popped everything from the stack, remove the stack attribute from the session.
+ //
+ if (isEmpty()) destroy();
+ return popped;
+ } else {
+ //
+ // We're discarding the popped page flow. Invoke its destroy() callback, unless it's longLived.
+ //
+ if (!popped.isLongLivedFlow()) popped.destroy();
+ }
+ }
+
+ destroy(); // we're empty -- remove the attribute from the session.
+ return null;
+ }
+
+ /**
+ * Pop page flows from the nesting stack until the given index.
+ *
+ * @return the last popped page flow of the given type.
+ */
+ PageFlowController popUntil(int index) {
+ for (int i = _stack.size() - 1; i > index; --i) {
+ pop();
+ }
+
+ return pop().getPageFlow();
+ }
+
+ int lastIndexOf(Class target) {
+ for (int i = _stack.size() - 1; i >= 0; --i) {
+ if (((PushedPageFlow) _stack.elementAt(i)).getPageFlow().getClass().equals(target)) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ void ensureFailover() {
+ StorageHandler sh = Handlers.get().getStorageHandler();
+ String attrName = InternalUtils.getScopedAttrName(JPF_STACK_ATTR);
+
+ sh.ensureFailover(attrName, this);
+ }
+
+ void save() {
+ StorageHandler sh = Handlers.get().getStorageHandler();
+ String attrName = InternalUtils.getScopedAttrName(JPF_STACK_ATTR);
+
+ sh.setAttribute(attrName, this);
+ }
+
+ private PageFlowStack() {
+ }
+
+ /**
+ * Push a page flow onto the stack of nested page flows in the session.
+ *
+ * @param pageFlow the page flow to push.
+ */
+ public void push(PageFlowController pageFlow) {
+ ActionInterceptorContext interceptorContext = ActionInterceptorContext.getActiveContext(true);
+
+ if (interceptorContext != null) {
+ ActionInterceptor interceptor = interceptorContext.getOverridingActionInterceptor();
+ InterceptorForward originalForward = interceptorContext.getOriginalForward();
+ String actionName = interceptorContext.getActionName();
+ _stack.push(new PushedPageFlow(pageFlow, interceptor, originalForward, actionName));
+ } else {
+ _stack.push(new PushedPageFlow(pageFlow));
+ }
+
+ // Tell the page flow that it is on the nesting stack.
+ pageFlow.setIsOnNestingStack(true);
+
+ // To ensure that this attribute is replicated for session failover.
+ ensureFailover();
+ }
+
+ /**
+ * Pop the most recently-pushed page flow from the stack of nested page flows in the session.
+ *
+ * @return a {@link PushedPageFlow} that represents the popped page flow.
+ */
+ public PushedPageFlow pop() {
+ PushedPageFlow ppf = (PushedPageFlow) _stack.pop();
+ PageFlowController pfc = ppf.getPageFlow();
+ pfc.setIsOnNestingStack(false);
+
+ // Reinitialize the page flow, in case it's lost its transient state.
+ pfc.reinitialize();
+ ensureFailover(); // to ensure that this attribute is replicated for session failover
+
+ return ppf;
+ }
+
+ /**
+ * Get the most recently-pushed page flow from the stack of nested page flows in the session.
+ *
+ * @return a {@link PushedPageFlow} that represents the page flow at the top of the stack.
+ */
+ public PushedPageFlow peek() {
+ return (PushedPageFlow) _stack.peek();
+ }
+
+ /**
+ * Tell whether the stack of nested page flows is empty.
+ *
+ * @return <code>true</code> if there are no nested page flows on the stack.
+ */
+ public boolean isEmpty() {
+ return _stack.isEmpty();
+ }
+
+ /**
+ * Get the size of the stack of nested page flows.
+ *
+ * @return the number of page flows that are currently (hidden away) on the stack.
+ */
+ public int size() {
+ return _stack.size();
+ }
+
+ /**
+ * @exclude
+ */
+ public void valueBound(HttpSessionBindingEvent event) {
+ }
+
+ /**
+ * @exclude
+ */
+ public void valueUnbound(HttpSessionBindingEvent event) {
+ if (_log.isDebugEnabled()) {
+ _log.debug("The page flow stack is being unbound from the session.");
+ }
+
+ while (!isEmpty()) {
+ PageFlowController jpf = pop().getPageFlow();
+
+ // Note that this page flow may have been serialized/deserialized, which will cause its transient info
+ // to be lost. Rehydrate it.
+ HttpSession session = event.getSession();
+ if (session != null) jpf.reinitialize();
+
+ if (!jpf.isLongLivedFlow()) jpf.destroy();
+ }
+ }
+
+ /**
+ * Get a stack of PageFlowControllers, not of PushedPageFlows.
+ */
+ Stack getLegacyStack() {
+ Stack ret = new Stack();
+
+ for (int i = 0; i < _stack.size(); ++i) {
+ ret.push(((PushedPageFlow) _stack.get(i)).getPageFlow());
+ }
+
+ return ret;
+ }
+}
Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/PageFlowUtils.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/PageFlowUtils.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/PageFlowUtils.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/PageFlowUtils.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,885 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import org.apache.commons.chain.web.WebContext;
+import org.apache.ti.core.ActionMessage;
+import org.apache.ti.core.urls.FreezableMutableURI;
+import org.apache.ti.core.urls.MutableURI;
+import org.apache.ti.core.urls.URIContext;
+import org.apache.ti.core.urls.URLRewriterService;
+import org.apache.ti.core.urls.URLType;
+import org.apache.ti.core.urltemplates.URLTemplatesFactory;
+import org.apache.ti.pageflow.handler.Handlers;
+import org.apache.ti.pageflow.handler.StorageHandler;
+import org.apache.ti.pageflow.internal.AdapterManager;
+import org.apache.ti.pageflow.internal.InternalConstants;
+import org.apache.ti.pageflow.internal.InternalUtils;
+import org.apache.ti.pageflow.internal.URIContextFactory;
+import org.apache.ti.pageflow.xwork.PageFlowActionContext;
+import org.apache.ti.schema.config.UrlConfig;
+import org.apache.ti.script.common.ImplicitObjectUtil;
+import org.apache.ti.util.config.ConfigUtil;
+import org.apache.ti.util.internal.FileUtils;
+import org.apache.ti.util.internal.InternalStringBuilder;
+import org.apache.ti.util.internal.concurrent.InternalConcurrentHashMap;
+import org.apache.ti.util.logging.Logger;
+
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Utility methods related to Page Flow.
+ */
+public class PageFlowUtils
+ implements PageFlowConstants, InternalConstants {
+
+ private static final Logger _log = Logger.getInstance(PageFlowUtils.class);
+
+ private static final String ACTION_PATH_ATTR = ATTR_PREFIX + "_actionPath";
+ private static final int PAGEFLOW_EXTENSION_LEN = PAGEFLOW_EXTENSION.length();
+ private static final String DEFAULT_AUTORESOLVE_EXTENSIONS[] = new String[]{ACTION_EXTENSION, PAGEFLOW_EXTENSION};
+
+
+ /**
+ * Map of Struts module prefix to Map of form-type-name to form-name.
+ */
+ private static Map/*< String, Map< String, List< String > > >*/ _formNameMaps =
+ new InternalConcurrentHashMap/*< String, Map< String, List< String > > >*/();
+
+
+ /**
+ * Get a URI for the "begin" action in the PageFlowController associated with the given
+ * request URI.
+ *
+ * @return a String that is the URI for the "begin" action in the PageFlowController associated
+ * with the given request URI.
+ */
+ public static String getBeginActionURI(String requestURI) {
+ // Translate this to a request for the begin action ("begin.do") for this PageFlowController.
+ InternalStringBuilder retVal = new InternalStringBuilder();
+ int lastSlash = requestURI.lastIndexOf('/');
+
+ if (lastSlash != -1) {
+ retVal.append(requestURI.substring(0, lastSlash));
+ }
+
+ retVal.append('/').append(BEGIN_ACTION_NAME).append(ACTION_EXTENSION);
+ return retVal.toString();
+ }
+
+ /**
+ * Get the {@link PageFlowController} that is nesting the current one.
+ *
+ * @return the nesting {@link PageFlowController}, or <code>null</code> if the current one
+ * is not being nested.
+ */
+ public static PageFlowController getNestingPageFlow() {
+ PageFlowStack jpfStack = PageFlowStack.get(false);
+
+ if (jpfStack != null && !jpfStack.isEmpty()) {
+ PageFlowController top = jpfStack.peek().getPageFlow();
+ return top;
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the current PageFlowController.
+ *
+ * @return the current PageFlowController from the user session, or <code>null</code>
+ * if there is none.
+ */
+ public static final PageFlowController getCurrentPageFlow() {
+ ActionResolver cur = getCurrentActionResolver();
+ return cur instanceof PageFlowController ? (PageFlowController) cur : null;
+ }
+
+ /**
+ * Get the current ActionResolver.
+ *
+ * @return the current ActionResolver from the user session, or <code>null</code> if there is none.
+ */
+ public static ActionResolver getCurrentActionResolver() {
+ StorageHandler sh = Handlers.get().getStorageHandler();
+
+ //
+ // First see if the current page flow is a long-lived, which is stored in its own attribute.
+ //
+ String currentLongLivedAttrName = InternalUtils.getScopedAttrName(CURRENT_LONGLIVED_ATTR);
+ String currentLongLivedNamespace = (String) sh.getAttribute(currentLongLivedAttrName);
+
+ if (currentLongLivedNamespace != null) {
+ return getLongLivedPageFlow(currentLongLivedNamespace);
+ } else {
+ String currentJpfAttrName = InternalUtils.getScopedAttrName(CURRENT_JPF_ATTR);
+ return (ActionResolver) sh.getAttribute(currentJpfAttrName);
+ }
+ }
+
+ /**
+ * Get the a map of shared flow name to shared flow instance, based on the names defined in the
+ * {@link org.apache.ti.pageflow.annotations.ti.controller#sharedFlowRefs sharedFlowRefs} attribute
+ * of the {@link org.apache.ti.pageflow.annotations.ti.controller @ti.controller} annotation on the
+ * <strong>current page flow</strong>.
+ *
+ * @return a Map of shared flow name (string) to shared flow instance ({@link SharedFlowController}).
+ */
+ public static Map/*< String, SharedFlowController >*/ getSharedFlows() {
+ Map/*< String, SharedFlowController >*/ sharedFlows = ImplicitObjectUtil.getSharedFlow();
+ return sharedFlows != null ? sharedFlows : Collections.EMPTY_MAP;
+ }
+
+ /**
+ * Get the shared flow with the given class name.
+ *
+ * @param sharedFlowClassName the class name of the shared flow to remove.
+ * @return the {@link SharedFlowController} of the given class name which is stored in the user session.
+ */
+ public static SharedFlowController getSharedFlow(String sharedFlowClassName) {
+ StorageHandler sh = Handlers.get().getStorageHandler();
+ return (SharedFlowController) sh.getAttribute(SHARED_FLOW_ATTR_PREFIX + sharedFlowClassName);
+ }
+
+ /**
+ * Destroy the current {@link SharedFlowController} of the given class name.
+ *
+ * @param sharedFlowClassName the class name of the current SharedFlowController to destroy.
+ */
+ public static void removeSharedFlow(String sharedFlowClassName) {
+ StorageHandler sh = Handlers.get().getStorageHandler();
+ sh.removeAttribute(SHARED_FLOW_ATTR_PREFIX + sharedFlowClassName);
+ }
+
+ /**
+ * Remove a "long-lived" page flow from the session. Once it is created, a long-lived page flow
+ * is never removed from the session unless this method or {@link PageFlowController#remove} is
+ * called. Navigating to another page flow hides the current long-lived controller, but does not
+ * remove it.
+ */
+ public static void removeLongLivedPageFlow(String namespace) {
+ StorageHandler sh = Handlers.get().getStorageHandler();
+ String attrName = InternalUtils.getLongLivedFlowAttr(namespace);
+ attrName = InternalUtils.getScopedAttrName(attrName);
+ sh.removeAttribute(attrName);
+
+ //
+ // Now, if the current page flow is long-lived, remove the reference.
+ //
+ String currentLongLivedAttrName = InternalUtils.getScopedAttrName(CURRENT_LONGLIVED_ATTR);
+ String currentLongLivedNamespace = (String) sh.getAttribute(currentLongLivedAttrName);
+
+ if (namespace.equals(currentLongLivedNamespace)) {
+ sh.removeAttribute(currentLongLivedAttrName);
+ }
+ }
+
+ /**
+ * Get the long-lived page flow instance associated with the given module (directory) path.
+ *
+ * @param namespace the namespace of the long-lived page flow.
+ * @return the long-lived page flow instance associated with the given module, or <code>null</code> if none is found.
+ */
+ public static PageFlowController getLongLivedPageFlow(String namespace) {
+ StorageHandler sh = Handlers.get().getStorageHandler();
+ String attrName = InternalUtils.getLongLivedFlowAttr(namespace);
+ attrName = InternalUtils.getScopedAttrName(attrName);
+ PageFlowController retVal = (PageFlowController) sh.getAttribute(attrName);
+ return retVal;
+ }
+
+ /**
+ * Make any form beans in the given {@link Forward} object available as attributets in the
+ * request/session (as appropriate).
+ *
+ * @param fwd the {@link Forward} object that contains the ActionForm instances to be
+ * made available in the request/session (as appropriate).
+ */
+ public static void setOutputForms(Forward fwd, boolean overwrite) {
+ if (fwd != null) {
+ setOutputForms(fwd.getOutputForms(), overwrite);
+ InternalUtils.setForwardedFormBean(fwd.getFirstOutputForm());
+ }
+ }
+
+ /**
+ * Make a set of form beans available as attributets in the request/session (as appropriate).
+ *
+ * @param outputForms an array of Object instances to be made available in the
+ * request/session (as appropriate).
+ */
+ public static void setOutputForms(Object[] outputForms) {
+ setOutputForms(outputForms, true);
+ }
+
+ /**
+ * Make a set of form beans available as attributets in the request/session (as appropriate).
+ *
+ * @param outputForms an array of Object instances to be made available in the
+ * request/session (as appropriate).
+ * @param overwrite if <code>false</code> a form from <code>fwd</code> will only be set
+ * in the request if there is no existing form with the same name.
+ */
+ public static void setOutputForms(Object[] outputForms, boolean overwrite) {
+ for (int i = 0; i < outputForms.length; ++i) {
+ setOutputForm(outputForms[i], overwrite);
+ }
+ }
+
+ /**
+ * Make a form bean available as an attribute in the request/session (as appropriate).
+ *
+ * @param formBean an Object instance to be made available in the request/session
+ * (as appropriate).
+ * @param overwrite if <code>false</code> a form from <code>fwd</code> will only be set
+ * in the request if there is no existing form with the same name.
+ */
+ public static void setOutputForm(Object formBean, boolean overwrite) {
+ if (formBean != null) {
+ PageFlowActionContext actionContext = PageFlowActionContext.get();
+ ModuleConfig moduleConfig = actionContext.getModuleConfig();
+ Class formClass = formBean.getClass();
+
+ //
+ // Get the names of *all* form beans of the desired type, and blast out this instance under all those names.
+ //
+ Map formBeanAttrNames = moduleConfig.getFormBeanAttributeNames();
+ List formNames = (List) formBeanAttrNames.get(formClass.getName());
+ List additionalFormNames = null;
+
+ //
+ // formNames is a statically-scoped list. Below, we create a dynamic list of form names that correspond
+ // to *implemented interfaces* of the given form bean class.
+ //
+ Class[] interfaces = formClass.getInterfaces();
+ for (int i = 0; i < interfaces.length; i++) {
+ Class formInterface = interfaces[i];
+ List toAdd = (List) formBeanAttrNames.get(formInterface.getName());
+ if (toAdd != null) {
+ if (additionalFormNames == null) additionalFormNames = new ArrayList();
+ additionalFormNames.addAll(toAdd);
+ }
+ }
+
+ if (formNames == null && additionalFormNames == null) {
+ String formName = generateFormBeanName(formClass);
+ InternalUtils.setFormInScope(formName, formBean, overwrite);
+ } else {
+ if (formNames != null) {
+ for (Iterator i = formNames.iterator(); i.hasNext();) {
+ String formName = (String) i.next();
+ InternalUtils.setFormInScope(formName, formBean, overwrite);
+ }
+ }
+
+ if (additionalFormNames != null) {
+ for (Iterator i = additionalFormNames.iterator(); i.hasNext();) {
+ String formName = (String) i.next();
+ InternalUtils.setFormInScope(formName, formBean, overwrite);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the name for the type of a Object instance. Use a name looked up from
+ * the current Struts module, or, if none is found, create one.
+ *
+ * @param formInstance the Object instance whose type will determine the name.
+ * @return the name found in the Struts module, or, if none is found, a name that is either:
+ * <ul>
+ * <li>a camel-cased version of the base class name (minus any package or outer-class
+ * qualifiers, or, if that name is already taken,</li>
+ * <li>the full class name, with '.' and '$' replaced by '_'.</li>
+ * </ul>
+ */
+ public static String getFormBeanName(Object formInstance) {
+ return getFormBeanName(formInstance.getClass());
+ }
+
+ /**
+ * Get the name for an Object type. Use a name looked up from the current Struts module, or,
+ * if none is found, create one.
+ *
+ * @param formBeanClass the Object-derived class whose type will determine the name.
+ * @return the name found in the Struts module, or, if none is found, a name that is either:
+ * <ul>
+ * <li>a camel-cased version of the base class name (minus any package or outer-class
+ * qualifiers, or, if that name is already taken,</li>
+ * <li>the full class name, with '.' and '$' replaced by '_'.</li>
+ * </ul>
+ */
+ public static String getFormBeanName(Class formBeanClass) {
+ ModuleConfig moduleConfig = PageFlowActionContext.get().getModuleConfig();
+ List names = (List) moduleConfig.getFormBeanAttributeNames().get(formBeanClass.getName());
+
+ if (names != null) {
+ assert names.size() > 0; // getFormNamesFromModuleConfig returns null or a nonempty list
+ return (String) names.get(0);
+ }
+
+ return generateFormBeanName(formBeanClass);
+ }
+
+ /**
+ * Create the name for a form bean type.
+ *
+ * @param formBeanClass the class whose type will determine the name.
+ * @return the name found in the Struts module, or, if none is found, a name that is either:
+ * <ul>
+ * <li>a camel-cased version of the base class name (minus any package or outer-class
+ * qualifiers, or, if that name is already taken,</li>
+ * <li>the full class name, with '.' and '$' replaced by '_'.</li>
+ * </ul>
+ */
+ private static String generateFormBeanName(Class formBeanClass) {
+ ModuleConfig moduleConfig = PageFlowActionContext.get().getModuleConfig();
+ String formBeanClassName = formBeanClass.getName();
+
+ //
+ // A form-bean wasn't found for this type, so we'll create a name. First try and create
+ // name that is a camelcased version of the classname without all of its package/outer-class
+ // qualifiers. If one with that name already exists, munge the fully-qualified classname.
+ //
+ String formType = formBeanClassName;
+ int lastQualifier = formType.lastIndexOf('$');
+
+ if (lastQualifier == -1) {
+ lastQualifier = formType.lastIndexOf('.');
+ }
+
+ String formName = formType.substring(lastQualifier + 1);
+ formName = Character.toLowerCase(formName.charAt(0)) + formName.substring(1);
+
+ if (moduleConfig.getFormBeans().get(formName) != null) {
+ formName = formType.replace('.', '_').replace('$', '_');
+ assert moduleConfig.getFormBeans().get(formName) == null : formName;
+ }
+
+ return formName;
+ }
+
+ /**
+ * Get the class name of a {@link PageFlowController}, given the URI to it.
+ *
+ * @param uri the URI to the {@link PageFlowController}, which should be relative to the
+ * web application root (i.e., it should not include the context path).
+ */
+ public static String getPageFlowClassName(String uri) {
+ assert uri != null;
+ assert uri.length() > 0;
+
+ if (uri.charAt(0) == '/') uri = uri.substring(1);
+
+ assert FileUtils.osSensitiveEndsWith(uri, PAGEFLOW_EXTENSION) : uri;
+ if (FileUtils.osSensitiveEndsWith(uri, PAGEFLOW_EXTENSION)) {
+ uri = uri.substring(0, uri.length() - PAGEFLOW_EXTENSION_LEN);
+ }
+
+ return uri.replace('/', '.');
+ }
+
+ /**
+ * Get the URI for a {@link PageFlowController}, given its class name.
+ *
+ * @param className the name of the {@link PageFlowController} class.
+ * @return a String that is the URI for the {@link PageFlowController}, relative to the web
+ * application root (i.e., not including the context path).
+ */
+ public static String getPageFlowURI(String className) {
+ return '/' + className.replace('.', '/') + PAGEFLOW_EXTENSION;
+ }
+
+ /**
+ * Get the most recent action URI that was processed by {@link FlowController#execute}.
+ *
+ * @return a String that is the most recent action URI. This is only valid during a request
+ * that has been forwarded from the action URI.
+ */
+ public static String getActionPath() {
+ return (String) getWebContext().getRequestScope().get(ACTION_PATH_ATTR);
+ }
+
+ private static WebContext getWebContext() {
+ return PageFlowActionContext.get().getWebContext();
+ }
+
+ /**
+ * Sets the most recent action URI that was processed by {@link FlowController#execute}.
+ */
+ static void setActionPath() {
+ PageFlowActionContext actionContext = PageFlowActionContext.get();
+ getWebContext().getRequestScope().put(ACTION_PATH_ATTR, actionContext.getRequestPath());
+ }
+
+ /**
+ * Tell whether a web application resource requires a secure transport protocol. This is
+ * determined from web.xml; for example, the following block specifies that all resources under
+ * /login require a secure transport protocol.
+ * <pre>
+ * <security-constraint>
+ * <web-resource-collection>
+ * <web-resource-name>Secure PageFlow - begin</web-resource-name>
+ * <url-pattern>/login/*</url-pattern>
+ * </web-resource-collection>
+ * <user-data-constraint>
+ * <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+ * </user-data-constraint>
+ * </security-constraint>
+ * </pre>
+ *
+ * @param uri a webapp-relative URI for a resource. There must not be query parameters or a scheme
+ * on the URI.
+ * @return <code>Boolean.TRUE</code> if a transport-guarantee of <code>CONFIDENTIAL</code> or
+ * <code>INTEGRAL</code> is associated with the given resource; <code>Boolean.FALSE</code>
+ * a transport-guarantee of <code>NONE</code> is associated with the given resource; or
+ * <code>null</code> if there is no transport-guarantee associated with the given resource.
+ */
+ public static SecurityProtocol getSecurityProtocol(String uri) {
+ return AdapterManager.getContainerAdapter().getSecurityProtocol(uri);
+ }
+
+ /**
+ * Set a named action output, which corresponds to an input declared by the <code>pageInput</code> JSP tag.
+ * The actual value can be read from within a JSP using the <code>"pageInput"</code> databinding context.
+ *
+ * @param name the name of the action output.
+ * @param value the value of the action output.
+ */
+ public static void addActionOutput(String name, Object value) {
+ Map map = InternalUtils.getActionOutputMap(true);
+
+ if (map.containsKey(name)) {
+ if (_log.isWarnEnabled()) {
+ _log.warn("Overwriting action output\"" + name + "\".");
+ }
+ }
+
+ map.put(name, value);
+ }
+
+ /**
+ * Get a named action output that was registered in the current request.
+ *
+ * @param name the name of the action output.
+ * @see #addActionOutput
+ */
+ public static Object getActionOutput(String name) {
+ Map map = InternalUtils.getActionOutputMap(false);
+ return map != null ? map.get(name) : null;
+ }
+
+ /**
+ * Add a property-related message that will be shown with the Errors and Error tags.
+ *
+ * @param propertyName the name of the property with which to associate this error.
+ * @param messageKey the message-resources key for the message.
+ * @param messageArgs zero or more arguments to the message.
+ */
+ public static void addActionError(String propertyName, String messageKey, Object[] messageArgs) {
+ InternalUtils.addActionError(propertyName, new ActionMessage(messageKey, messageArgs));
+ }
+
+ /**
+ * Add a property-related message that will be shown with the Errors and Error tags.
+ *
+ * @param propertyName the name of the property with which to associate this error.
+ * @param messageKey the message-resources key for the message.
+ */
+ public static void addActionError(String propertyName, String messageKey) {
+ InternalUtils.addActionError(propertyName, new ActionMessage(messageKey, null));
+ }
+
+ /**
+ * Add a property-related message that will be shown with the Errors and Error tags.
+ *
+ * @param propertyName the name of the property with which to associate this error.
+ * @param messageKey the message-resources key for the message.
+ * @param messageArg an argument to the message
+ */
+ public static void addActionError(String propertyName, String messageKey, Object messageArg) {
+ Object[] messageArgs = new Object[]{messageArg};
+ InternalUtils.addActionError(propertyName, new ActionMessage(messageKey, messageArgs));
+ }
+
+ /**
+ * Add a property-related message that will be shown with the Errors and Error tags.
+ *
+ * @param propertyName the name of the property with which to associate this error.
+ * @param messageKey the message-resources key for the message.
+ * @param messageArg1 the first argument to the message
+ * @param messageArg2 the second argument to the message
+ */
+ public static void addActionError(String propertyName, String messageKey, Object messageArg1, Object messageArg2) {
+ Object[] messageArgs = new Object[]{messageArg1, messageArg2};
+ InternalUtils.addActionError(propertyName, new ActionMessage(messageKey, messageArgs));
+ }
+
+ /**
+ * Add a property-related message that will be shown with the Errors and Error tags.
+ *
+ * @param propertyName the name of the property with which to associate this error.
+ * @param messageKey the message-resources key for the message.
+ * @param messageArg1 the first argument to the message
+ * @param messageArg2 the second argument to the message
+ * @param messageArg3 the third argument to the message
+ */
+ public static void addActionError(String propertyName, String messageKey, Object messageArg1, Object messageArg2, Object messageArg3) {
+ Object[] messageArgs = new Object[]{messageArg1, messageArg2, messageArg3};
+ InternalUtils.addActionError(propertyName, new ActionMessage(messageKey, messageArgs));
+ }
+
+ /**
+ * Add a property-related message as an expression that will be evaluated and shown with the Errors and Error tags.
+ *
+ * @param propertyName the name of the property with which to associate this error.
+ * @param expression the expression that will be evaluated to generate the error message.
+ * @param messageArgs zero or more arguments to the message.
+ */
+ public static void addActionErrorExpression(String propertyName, String expression, Object[] messageArgs) {
+ ExpressionMessage msg = new ExpressionMessage(expression, messageArgs);
+ InternalUtils.addActionError(propertyName, msg);
+ }
+
+ /**
+ * Resolve the given action to a URI by running an entire request-processing cycle on the given ScopedRequest
+ * and ScopedResponse.
+ * @exclude
+ *
+ * @param actionOverride if not <code>null</code>, this qualified action-path is used to construct an action
+ * URI which is set as the request URI. The action-path <strong>must</strong> begin with '/',
+ * which makes it qualified from the webapp root.
+ * @param autoResolveExtensions a list of URI extensions (e.g., ".do", ".jpf") that will be auto-resolved, i.e.,
+ * on which this method will be recursively called. If <code>null</code>, the
+ * default extensions ".do" and ".jpf" will be used.
+ */
+ /* TODO: re-add some form of this, for portal/portlet support
+ public static ActionResult strutsLookup( String actionOverride, String[] autoResolveExtensions )
+ throws Exception
+ {
+ ScopedRequest scopedRequest = ScopedUtils.unwrapRequest( request );
+ ScopedResponse scopedResponse = ScopedUtils.unwrapResponse( response );
+ assert scopedRequest != null : request.getClass().getName();
+ assert scopedResponse != null : response.getClass().getName();
+
+ if ( scopedRequest == null )
+ {
+ throw new IllegalArgumentException( "request must be of type " + ScopedRequest.class.getName() );
+ }
+ if ( scopedResponse == null )
+ {
+ throw new IllegalArgumentException( "response must be of type " + ScopedResponse.class.getName() );
+ }
+
+ ActionServlet as = InternalUtils.getActionServlet( context );
+
+ if ( as == null )
+ {
+ _log.error( "There is no initialized ActionServlet. The ActionServlet must be set to load-on-startup." );
+ return null;
+ }
+
+ if ( actionOverride != null )
+ {
+ // The action must be fully-qualified with its namespace.
+ assert actionOverride.charAt( 0 ) == '/' : actionOverride;
+ InternalStringBuilder uri = new InternalStringBuilder( scopedRequest.getContextPath() );
+ uri.append( actionOverride );
+ uri.append( PageFlowConstants.ACTION_EXTENSION );
+ scopedRequest.setRequestURI( uri.toString() );
+ }
+
+ //
+ // In case the request was already forwarded once, clear out the recorded forwarded-URI. This
+ // will allow us to tell whether processing the request actually forwarded somewhere.
+ //
+ scopedRequest.setForwardedURI( null );
+
+ //
+ // Now process the request. We create a PageFlowRequestWrapper for pageflow-specific request-scoped info.
+ //
+ PageFlowRequestWrapper wrappedRequest = PageFlowRequestWrapper.wrapRequest( ( HttpServletRequest ) request );
+ as.doGet( wrappedRequest, scopedResponse ); // this just calls process() -- same as doPost()
+
+ String returnURI;
+
+ if ( ! scopedResponse.didRedirect() )
+ {
+ returnURI = scopedRequest.getForwardedURI();
+
+ if ( autoResolveExtensions == null )
+ {
+ autoResolveExtensions = DEFAULT_AUTORESOLVE_EXTENSIONS;
+ }
+
+ if ( returnURI != null )
+ {
+ for ( int i = 0; i < autoResolveExtensions.length; ++i )
+ {
+ if ( FileUtils.uriEndsWith( returnURI, autoResolveExtensions[i] ) )
+ {
+ scopedRequest.doForward();
+ return strutsLookup( context, wrappedRequest, scopedResponse, null, autoResolveExtensions );
+ }
+ }
+ }
+ }
+ else
+ {
+ returnURI = scopedResponse.getRedirectURI();
+ }
+
+ DeferredSessionStorageHandler.applyChanges( scopedRequest, context );
+
+ if ( returnURI != null )
+ {
+ return new ActionResultImpl( returnURI, scopedResponse.didRedirect(), scopedResponse.getStatusCode(),
+ scopedResponse.getStatusMessage(), scopedResponse.isError() );
+ }
+ else
+ {
+ return null;
+ }
+ }
+ */
+
+ /**
+ * Create a raw action URI, which can be modified before being sent through the registered URL rewriting chain
+ * using {@link URLRewriterService#rewriteURL}. Use {@link #getRewrittenActionURI} to get a fully-rewritten URI.
+ *
+ * @param actionName the action name to convert into a MutableURI; may be qualified with a path from the webapp
+ * root, in which case the parent directory from the current request is <i>not</i> used.
+ * @return a MutableURI for the given action, suitable for URL rewriting.
+ * @throws URISyntaxException if there is a problem converting the action URI (derived from processing the given
+ * action name) into a MutableURI.
+ */
+ public static MutableURI getActionURI(String actionName)
+ throws URISyntaxException {
+ // TODO: need ActionMapper to be reversible -- it should construct the URI.
+ if (actionName.length() < 1) throw new IllegalArgumentException("actionName must be non-empty");
+
+ PageFlowActionContext actionContext = PageFlowActionContext.get();
+ InternalStringBuilder actionURI = new InternalStringBuilder(actionContext.getRequestContextPath());
+
+ if (actionName.charAt(0) != '/') {
+ actionURI.append(actionContext.getNamespace());
+ actionURI.append('/');
+ }
+
+ actionURI.append(actionName);
+ if (!actionName.endsWith(ACTION_EXTENSION)) actionURI.append(ACTION_EXTENSION);
+
+ FreezableMutableURI uri = new FreezableMutableURI();
+ // TODO: re-add the following line, using some abstraction
+ //uri.setEncoding( response.getCharacterEncoding() );
+ uri.setURI(actionURI.toString(), true);
+ return uri;
+ }
+
+ /**
+ * Create a fully-rewritten URI given an action name and parameters.
+ *
+ * @param actionName the action name to convert into a fully-rewritten URI; may be qualified with a path from the
+ * webapp root, in which case the parent directory from the current request is <i>not</i> used.
+ * @param params the additional parameters to include in the URI query.
+ * @param fragment the fragment (anchor or location) for this url.
+ * @param forXML flag indicating that the query of the uri should be written
+ * using the "&amp;" entity, rather than the character, '&'.
+ * @return a fully-rewritten URI for the given action.
+ * @throws URISyntaxException if there is a problem converting the action URI (derived
+ * from processing the given action name) into a MutableURI.
+ */
+ public static String getRewrittenActionURI(String actionName, Map params, String fragment, boolean forXML)
+ throws URISyntaxException {
+ MutableURI uri = getActionURI(actionName);
+ if (params != null) uri.addParameters(params, false);
+ if (fragment != null) uri.setFragment(uri.encode(fragment));
+
+ boolean needsToBeSecure = needsToBeSecure(uri.getPath(), true);
+ URLRewriterService.rewriteURL(uri, URLType.ACTION, needsToBeSecure);
+ String key = getURLTemplateKey(URLType.ACTION, needsToBeSecure);
+ URIContext uriContext = URIContextFactory.getInstance(forXML);
+
+ return URLRewriterService.getTemplatedURL(uri, key, uriContext);
+ }
+
+ /**
+ * Create a fully-rewritten URI given a path and parameters.
+ * <p/>
+ * <p> Calls the rewriter service using a type of {@link URLType#RESOURCE}. </p>
+ *
+ * @param path the path to process into a fully-rewritten URI.
+ * @param params the additional parameters to include in the URI query.
+ * @param fragment the fragment (anchor or location) for this URI.
+ * @param forXML flag indicating that the query of the uri should be written
+ * using the "&amp;" entity, rather than the character, '&'.
+ * @return a fully-rewritten URI for the given action.
+ * @throws URISyntaxException if there's a problem converting the action URI (derived
+ * from processing the given action name) into a MutableURI.
+ */
+ public static String getRewrittenResourceURI(String path, Map params,
+ String fragment, boolean forXML)
+ throws URISyntaxException {
+ return rewriteResourceOrHrefURL(path, params, fragment, forXML, URLType.RESOURCE);
+ }
+
+ /**
+ * Create a fully-rewritten URI given a path and parameters.
+ * <p/>
+ * <p> Calls the rewriter service using a type of {@link URLType#ACTION}. </p>
+ *
+ * @param path the path to process into a fully-rewritten URI.
+ * @param params the additional parameters to include in the URI query.
+ * @param fragment the fragment (anchor or location) for this URI.
+ * @param forXML flag indicating that the query of the uri should be written
+ * using the "&amp;" entity, rather than the character, '&'.
+ * @return a fully-rewritten URI for the given action.
+ * @throws URISyntaxException if there's a problem converting the action URI (derived
+ * from processing the given action name) into a MutableURI.
+ */
+ public static String getRewrittenHrefURI(String path, Map params, String fragment, boolean forXML)
+ throws URISyntaxException {
+ return rewriteResourceOrHrefURL(path, params, fragment, forXML, URLType.ACTION);
+ }
+
+ private static String rewriteResourceOrHrefURL(String path, Map params, String fragment, boolean forXML, URLType urlType)
+ throws URISyntaxException {
+ boolean encoded = false;
+ UrlConfig urlConfig = ConfigUtil.getConfig().getUrlConfig();
+
+ if (urlConfig != null && urlConfig.isSetUrlEncodeUrls()) {
+ encoded = !urlConfig.getUrlEncodeUrls();
+ }
+
+ FreezableMutableURI uri = new FreezableMutableURI();
+ // TODO: re-add the following line, using some abstraction
+ //uri.setEncoding( response.getCharacterEncoding() );
+ uri.setURI(path, encoded);
+
+ if (params != null) {
+ uri.addParameters(params, false);
+ }
+
+ if (fragment != null) {
+ uri.setFragment(uri.encode(fragment));
+ }
+
+ URIContext uriContext = URIContextFactory.getInstance(forXML);
+ if (uri.isAbsolute()) {
+ return uri.getURIString(uriContext);
+ }
+
+ if (path.length() != 0 && path.charAt(0) != '/') {
+ PageFlowActionContext actionContext = PageFlowActionContext.get();
+ String reqPath = actionContext.getRequestPath();
+ reqPath = reqPath.substring(0, reqPath.lastIndexOf('/') + 1);
+ uri.setPath(reqPath + uri.getPath());
+ }
+
+ boolean needsToBeSecure = needsToBeSecure(uri.getPath(), true);
+ URLRewriterService.rewriteURL(uri, urlType, needsToBeSecure);
+ String key = getURLTemplateKey(urlType, needsToBeSecure);
+
+ return URLRewriterService.getTemplatedURL(uri, key, uriContext);
+ }
+
+ /**
+ * Tell whether a given URI should be written to be secure.
+ *
+ * @param uri the URI to check.
+ * @param stripContextPath if <code>true</code>, strip the webapp context path from the URI before
+ * processing it.
+ * @return <code>true</code> when:
+ * <ul>
+ * <li>the given URI is configured in the deployment descriptor to be secure (according to
+ * {@link SecurityProtocol}), or
+ * <li>the given URI is not configured in the deployment descriptor, and the current request
+ * is secure ({@link javax.servlet.http.HttpServletRequest#isSecure} returns
+ * <code>true</code>).
+ * </ul>
+ * <code>false</code> when:
+ * <ul>
+ * <li>the given URI is configured explicitly in the deployment descriptor to be unsecure
+ * (according to {@link SecurityProtocol}), or
+ * <li>the given URI is not configured in the deployment descriptor, and the current request
+ * is unsecure ({@link javax.servlet.http.HttpServletRequest#isSecure} returns
+ * <code>false</code>).
+ * </ul>
+ */
+ public static boolean needsToBeSecure(String uri, boolean stripContextPath) {
+ PageFlowActionContext actionContext = PageFlowActionContext.get();
+
+ // Get the web-app relative path for security check
+ String secureCheck = uri;
+ if (stripContextPath) {
+ String contextPath = actionContext.getRequestContextPath();
+ if (secureCheck.startsWith(contextPath)) {
+ secureCheck = secureCheck.substring(contextPath.length());
+ }
+ }
+
+ boolean secure = false;
+ if (secureCheck.indexOf('?') > -1) {
+ secureCheck = secureCheck.substring(0, secureCheck.indexOf('?'));
+ }
+
+ SecurityProtocol sp = getSecurityProtocol(secureCheck);
+ if (sp.equals(SecurityProtocol.UNSPECIFIED)) {
+ secure = actionContext.isRequestSecure();
+ } else {
+ secure = sp.equals(SecurityProtocol.SECURE);
+ }
+
+ return secure;
+ }
+
+ /**
+ * Returns a key for the URL template type given the URL type and a
+ * flag indicating a secure URL or not.
+ *
+ * @param urlType the type of URL (ACTION, RESOURCE).
+ * @param needsToBeSecure indicates that the template should be for a secure URL.
+ * @return the key/type of template to use.
+ */
+ public static String getURLTemplateKey(URLType urlType, boolean needsToBeSecure) {
+ String key = URLTemplatesFactory.ACTION_TEMPLATE;
+ if (urlType.equals(URLType.ACTION)) {
+ if (needsToBeSecure) {
+ key = URLTemplatesFactory.SECURE_ACTION_TEMPLATE;
+ } else {
+ key = URLTemplatesFactory.ACTION_TEMPLATE;
+ }
+ } else if (urlType.equals(URLType.RESOURCE)) {
+ if (needsToBeSecure) {
+ key = URLTemplatesFactory.SECURE_RESOURCE_TEMPLATE;
+ } else {
+ key = URLTemplatesFactory.RESOURCE_TEMPLATE;
+ }
+ }
+
+ return key;
+ }
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org