0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: * $Header:$
0018: */
0019: package org.apache.beehive.netui.pageflow;
0020:
0021: import org.apache.beehive.netui.core.urls.MutableURI;
0022: import org.apache.beehive.netui.pageflow.config.PageFlowActionMapping;
0023: import org.apache.beehive.netui.pageflow.config.PageFlowExceptionConfig;
0024: import org.apache.beehive.netui.pageflow.handler.ActionForwardHandler;
0025: import org.apache.beehive.netui.pageflow.handler.ExceptionsHandler;
0026: import org.apache.beehive.netui.pageflow.handler.FlowControllerHandlerContext;
0027: import org.apache.beehive.netui.pageflow.handler.Handlers;
0028: import org.apache.beehive.netui.pageflow.handler.LoginHandler;
0029: import org.apache.beehive.netui.pageflow.internal.*;
0030: import org.apache.beehive.netui.pageflow.scoping.ScopedRequest;
0031: import org.apache.beehive.netui.util.internal.FileUtils;
0032: import org.apache.beehive.netui.util.internal.InternalStringBuilder;
0033: import org.apache.beehive.netui.util.internal.cache.ClassLevelCache;
0034: import org.apache.beehive.netui.util.logging.Logger;
0035: import org.apache.struts.Globals;
0036: import org.apache.struts.action.ActionForm;
0037: import org.apache.struts.action.ActionForward;
0038: import org.apache.struts.action.ActionMapping;
0039: import org.apache.struts.action.ActionMessage;
0040: import org.apache.struts.action.ActionMessages;
0041: import org.apache.struts.action.ActionServlet;
0042: import org.apache.struts.action.RequestProcessor;
0043: import org.apache.struts.config.ActionConfig;
0044: import org.apache.struts.config.ControllerConfig;
0045: import org.apache.struts.config.ModuleConfig;
0046: import org.apache.struts.util.MessageResources;
0047: import org.apache.struts.util.RequestUtils;
0048: import org.apache.struts.util.TokenProcessor;
0049:
0050: import javax.security.auth.login.LoginException;
0051: import javax.servlet.ServletContext;
0052: import javax.servlet.ServletException;
0053: import javax.servlet.http.HttpServletRequest;
0054: import javax.servlet.http.HttpServletResponse;
0055: import javax.servlet.http.HttpSession;
0056: import javax.sql.DataSource;
0057: import java.io.IOException;
0058: import java.lang.reflect.Field;
0059: import java.lang.reflect.InvocationTargetException;
0060: import java.lang.reflect.Method;
0061: import java.lang.reflect.Modifier;
0062: import java.net.URISyntaxException;
0063: import java.util.ArrayList;
0064: import java.util.Iterator;
0065: import java.util.Locale;
0066: import java.util.Map;
0067: import java.security.Principal;
0068:
0069: /**
0070: * Base class for user-written flow controllers - {@link PageFlowController}s and {@link SharedFlowController}s.
0071: */
0072: public abstract class FlowController extends PageFlowManagedObject
0073: implements PageFlowConstants, ActionResolver {
0074: private static final Logger _log = Logger
0075: .getInstance(FlowController.class);
0076:
0077: private static final String ONCREATE_EXCEPTION_FORWARD = InternalConstants.ATTR_PREFIX
0078: + "onCreateException";
0079: private static final String CACHEID_ACTION_METHODS = InternalConstants.ATTR_PREFIX
0080: + "actionMethods";
0081: private static final int DEFAULT_MAX_CONCURRENT_REQUEST_COUNT = 4;
0082: private static final String MAX_CONCURRENT_REQUESTS_PARAM = "pageflow-max-concurrent-requests";
0083: private static final int EXCEEDED_MAX_CONCURRENT_REQUESTS_ERRORCODE = 503;
0084: private static final Locale DEFAULT_LOCALE = Locale.getDefault();
0085: private static final ActionForward NULL_ACTION_FORWARD = new ActionForward();
0086: private static final TokenProcessor TOKEN_PROCESSOR = TokenProcessor
0087: .getInstance();
0088:
0089: /**
0090: * The system default Locale.
0091: *
0092: * @deprecated Use {@link #getDefaultLocale}.
0093: */
0094: protected static Locale defaultLocale = DEFAULT_LOCALE;
0095:
0096: static class PerRequestState {
0097: private HttpServletRequest _request;
0098: private HttpServletResponse _response;
0099: private ActionMapping _actionMapping;
0100:
0101: public PerRequestState(HttpServletRequest request,
0102: HttpServletResponse response,
0103: ActionMapping actionMapping) {
0104: _request = request;
0105: _response = response;
0106: _actionMapping = actionMapping;
0107: }
0108:
0109: public HttpServletRequest getRequest() {
0110: return _request;
0111: }
0112:
0113: public HttpServletResponse getResponse() {
0114: return _response;
0115: }
0116:
0117: public ActionMapping getActionMapping() {
0118: return _actionMapping;
0119: }
0120: }
0121:
0122: /**
0123: * Stores per-request state, which is <i>only valid during calls to {@link FlowController#execute} or {@link FlowController#handleException}</i>.
0124: */
0125: private transient PerRequestState _perRequestState;
0126:
0127: /**
0128: * Cached reference to the associated Struts ModuleConfig.
0129: */
0130: private transient ModuleConfig _moduleConfig = null;
0131:
0132: /**
0133: * @see #incrementRequestCount
0134: */
0135: private transient int _requestCount = 0;
0136:
0137: /**
0138: * @see #incrementRequestCount
0139: */
0140: private static int _maxConcurrentRequestCount = -1;
0141:
0142: /**
0143: * Default constructor.
0144: */
0145: protected FlowController() {
0146: }
0147:
0148: /**
0149: * Reinitialize the object for a new request. Used by the framework; normally should not be called directly.
0150: */
0151: public void reinitialize(HttpServletRequest request,
0152: HttpServletResponse response, ServletContext servletContext) {
0153: //
0154: // Cache the associated ModuleConfig. This is used throughout the code, in places where the request
0155: // isn't available to do a lazy initialization.
0156: //
0157: super .reinitialize(request, response, servletContext);
0158: }
0159:
0160: /**
0161: * Log in the user, using "weak" username/password authentication. Goes through a custom {@link LoginHandler}, if
0162: * one has been configured in beehive-netui-config.xml.
0163: *
0164: * @param username the user's login name
0165: * @param password the user's password
0166: *
0167: * @exception LoginException if the authentication failed
0168: */
0169: public void login(String username, String password)
0170: throws LoginException {
0171: LoginHandler lh = Handlers.get(getServletContext())
0172: .getLoginHandler();
0173: lh.login(getHandlerContext(), username, password);
0174: }
0175:
0176: /**
0177: * Log out the current user. Goes through a custom {@link LoginHandler}, if one has been configured in
0178: * beehive-netui-config.xml.
0179: *
0180: * @param invalidateSessions if true, the session is invalidated (on all single-signon webapps);
0181: * otherwise the session and its data are left intact (except for authentication
0182: * information used internally by the server). To invalidate the session in only the
0183: * current webapp, set this parameter to <code>false</code> and call
0184: * {@link FlowController#getSession}.invalidate().
0185: */
0186: public void logout(boolean invalidateSessions) {
0187: LoginHandler lh = Handlers.get(getServletContext())
0188: .getLoginHandler();
0189: lh.logout(getHandlerContext(), invalidateSessions);
0190: }
0191:
0192: /**
0193: * Get the current logged-in user. Goes through a custom {@link LoginHandler}, if one has been configured in
0194: * beehive-netui-config.xml.
0195: *
0196: * @return the current logged-in <code>Principal</code>, or <code>null</code> if there is no logged-in user.
0197: */
0198: public Principal getUserPrincipal() {
0199: LoginHandler lh = Handlers.get(getServletContext())
0200: .getLoginHandler();
0201: return lh.getUserPrincipal(getHandlerContext());
0202: }
0203:
0204: /**
0205: * Tell whether the current logged-in user is a member of a given role. Goes through a custom {@link LoginHandler},
0206: * if one has been configured in beehive-netui-config.xml.
0207: *
0208: * @param roleName the name of the role to test.
0209: * @return <code>true</code> if there is a current logged-in user and the user is a member of the given role.
0210: * @see #getUserPrincipal
0211: */
0212: public boolean isUserInRole(String roleName) {
0213: LoginHandler lh = Handlers.get(getServletContext())
0214: .getLoginHandler();
0215: return lh.isUserInRole(getHandlerContext(), roleName);
0216: }
0217:
0218: /**
0219: * Send a Page Flow error to the browser.
0220: *
0221: * @deprecated Use {@link FlowController#sendError(String, HttpServletRequest, HttpServletResponse)} instead.
0222: * @param errText the error message to display.
0223: * @param response the current HttpServletResponse.
0224: */
0225: protected void sendError(String errText,
0226: HttpServletResponse response) throws IOException {
0227: sendError(errText, null, response);
0228: }
0229:
0230: /**
0231: * Send a Page Flow error to the browser.
0232: *
0233: * @param errText the error message to display.
0234: * @param response the current HttpServletResponse.
0235: */
0236: protected void sendError(String errText,
0237: HttpServletRequest request, HttpServletResponse response)
0238: throws IOException {
0239: InternalUtils.sendError("PageFlow_Custom_Error", null, request,
0240: response, new Object[] { getDisplayName(), errText });
0241: }
0242:
0243: /**
0244: * Handle the given exception - invoke user code if appropriate and return a destination URI.
0245: *
0246: * @param ex the Exception to handle.
0247: * @param mapping the Struts action mapping for current Struts action being processed.
0248: * @param form the form-bean (if any) associated with the Struts action being processed. May be null.
0249: * @param request the current HttpServletRequest.
0250: * @param response the current HttpServletResponse.
0251: * @return a Struts ActionForward object that specifies the URI that should be displayed.
0252: * @throws ServletException if another Exception is thrown during handling of <code>ex</code>.
0253: */
0254: public synchronized ActionForward handleException(Throwable ex,
0255: ActionMapping mapping, ActionForm form,
0256: HttpServletRequest request, HttpServletResponse response)
0257: throws IOException, ServletException {
0258: PerRequestState prevState = setPerRequestState(new PerRequestState(
0259: request, response, mapping));
0260:
0261: try {
0262: ExceptionsHandler eh = Handlers.get(getServletContext())
0263: .getExceptionsHandler();
0264: FlowControllerHandlerContext context = getHandlerContext();
0265:
0266: // First, put the exception into the request (or other applicable context).
0267: Throwable unwrapped = eh.unwrapException(context, ex);
0268: eh.exposeException(context, unwrapped, mapping);
0269: return eh
0270: .handleException(context, unwrapped, mapping, form);
0271: } finally {
0272: setPerRequestState(prevState);
0273: }
0274: }
0275:
0276: /**
0277: * Get the name of the current action being executed. This call is only valid
0278: * during {@link FlowController#execute} (where any user action method is invoked), and during the lifecycle
0279: * methods {@link FlowController#beforeAction} and {@link FlowController#afterAction}.
0280: *
0281: * @return the name of the current action being executed.
0282: * @throws IllegalStateException if this method is invoked outside of action method
0283: * execution (i.e., outside of the call to {@link FlowController#execute}, and outside of
0284: * {@link FlowController#onCreate}, {@link FlowController#beforeAction}, {@link FlowController#afterAction}.
0285: */
0286:
0287: protected String getCurrentActionName() {
0288: return InternalUtils.getActionName(getActionMapping());
0289: }
0290:
0291: /**
0292: * Perform decision logic to determine the next URI to be displayed.
0293: *
0294: * @param mapping the Struts ActionMapping for the current action being processed.
0295: * @param form the form-bean (if any) associated with the Struts action being processed. May be null.
0296: * @param request the current HttpServletRequest.
0297: * @param response the current HttpServletResponse.
0298: * @return a Struts ActionForward object that specifies the next URI to be displayed.
0299: * @throws Exception if an Exception was thrown during user action-handling code.
0300: */
0301: public ActionForward execute(ActionMapping mapping,
0302: ActionForm form, HttpServletRequest request,
0303: HttpServletResponse response) throws Exception {
0304: //
0305: // Don't actually run the action (and perform the associated synchronization) if there are too many
0306: // concurrent requests to this instance.
0307: //
0308: if (incrementRequestCount(request, response,
0309: getServletContext())) {
0310: try {
0311: // netui:sync-point
0312: synchronized (this ) {
0313: ActionForward ret = null;
0314:
0315: // establish the control context for running the beginAction, Action, afterAction code
0316: PageFlowControlContainer pfcc = null;
0317: try {
0318: pfcc = PageFlowControlContainerFactory
0319: .getControlContainer(request,
0320: getServletContext());
0321: pfcc.beginContextOnPageFlow(this , request,
0322: response, getServletContext());
0323: } catch (Exception e) {
0324: return handleException(e, mapping, form,
0325: request, response);
0326: }
0327:
0328: try {
0329: // execute the beginAction, Action, afterAction code
0330: ret = internalExecute(mapping, form, request,
0331: response);
0332: } finally {
0333: try {
0334: pfcc.endContextOnPageFlow(this );
0335: } catch (Exception e) {
0336: // if already handling an exception during execute, then just log
0337: PageFlowRequestWrapper rw = PageFlowRequestWrapper
0338: .get(request);
0339: Throwable alreadyBeingHandled = rw
0340: .getExceptionBeingHandled();
0341: if (alreadyBeingHandled != null) {
0342: _log
0343: .error(
0344: "Exception thrown while ending context on page flow in execute()",
0345: e);
0346: } else {
0347: return handleException(e, mapping,
0348: form, request, response);
0349: }
0350: }
0351: }
0352: return ret;
0353: }
0354: } finally {
0355: decrementRequestCount(request);
0356: }
0357: } else {
0358: return null; // error was written to the response by incrementRequestCount()
0359: }
0360: }
0361:
0362: /**
0363: * Internal method used to execute an action on a FlowController. This is where the magic generally happens as
0364: * the {@link #beforeAction()}, login, action execution (simple and normal), and {@link #afterAction()} are
0365: * all executed by this method. This method is considered <i>internal</i> and should not be invoked direclty.
0366: */
0367: protected ActionForward internalExecute(ActionMapping mapping,
0368: ActionForm form, HttpServletRequest request,
0369: HttpServletResponse response) throws Exception {
0370: ServletContainerAdapter sca = AdapterManager
0371: .getServletContainerAdapter(getServletContext());
0372: PageFlowEventReporter eventReporter = sca.getEventReporter();
0373: RequestContext requestContext = new RequestContext(request,
0374: response);
0375: eventReporter.actionRaised(requestContext, this , mapping, form);
0376: long startTime = System.currentTimeMillis();
0377:
0378: //
0379: // If we handled an exception in onCreate, just forward to the result of that.
0380: //
0381: ActionForward onCreateFwd = (ActionForward) request
0382: .getAttribute(ONCREATE_EXCEPTION_FORWARD);
0383:
0384: if (onCreateFwd != null) {
0385: return onCreateFwd == NULL_ACTION_FORWARD ? null
0386: : onCreateFwd;
0387: }
0388:
0389: PageFlowUtils.setActionURI(request);
0390:
0391: //
0392: // First change the actionPath (path) so that it lines up with our naming convention
0393: // for action methods.
0394: //
0395: boolean gotPastBeforeAction = false;
0396: ServletContext servletContext = getServletContext();
0397: PerRequestState prevState = setPerRequestState(new PerRequestState(
0398: request, response, mapping));
0399:
0400: try {
0401: //
0402: // beforeAction callback
0403: //
0404: beforeAction();
0405: gotPastBeforeAction = true;
0406:
0407: PageFlowActionMapping pfActionMapping = mapping instanceof PageFlowActionMapping ? (PageFlowActionMapping) mapping
0408: : null;
0409: Object unwrappedForm = InternalUtils.unwrapFormBean(form);
0410:
0411: //
0412: // mapping.isOverloaded() means it's the base mapping for a set of overloaded mappings.
0413: // Find the one appropriate to the passed-in form.
0414: //
0415: if (unwrappedForm != null && pfActionMapping != null) {
0416: if (pfActionMapping.isOverloaded()) {
0417: String mappingPath = pfActionMapping.getPath();
0418:
0419: //
0420: // Try the form class and all superclasses to get an overloaded action path.
0421: //
0422: for (Class i = unwrappedForm.getClass(); i != null; i = i
0423: .getSuperclass()) {
0424: String formQualifiedActionPath = getFormQualifiedActionPath(
0425: i, mappingPath);
0426: ActionConfig cf = pfActionMapping
0427: .getModuleConfig().findActionConfig(
0428: formQualifiedActionPath);
0429:
0430: if (cf != null) {
0431: assert cf instanceof PageFlowActionMapping : cf
0432: .getClass().getName();
0433:
0434: if (_log.isDebugEnabled()) {
0435: _log
0436: .debug("Found form-specific mapping "
0437: + cf.getPath()
0438: + " -- choosing this one over current mapping "
0439: + mappingPath);
0440: }
0441:
0442: pfActionMapping = (PageFlowActionMapping) cf;
0443: mapping = pfActionMapping;
0444: break;
0445: }
0446: }
0447: }
0448: }
0449:
0450: String actionName = InternalUtils.getActionName(mapping);
0451:
0452: //
0453: // Check whether isLoginRequired=true for this action.
0454: //
0455: LoginHandler loginHandler = Handlers.get(
0456: getServletContext()).getLoginHandler();
0457:
0458: if (pfActionMapping != null
0459: && pfActionMapping.isLoginRequired()
0460: && loginHandler
0461: .getUserPrincipal(getHandlerContext()) == null) {
0462: NotLoggedInException ex = createNotLoggedInException(
0463: actionName, request);
0464: return handleException(ex, mapping, form, request,
0465: response);
0466: }
0467:
0468: //
0469: // Now delegate to the appropriate action method, or if it's a simple action, handle it that way.
0470: //
0471: ActionForward retVal;
0472: if (pfActionMapping != null
0473: && pfActionMapping.isSimpleAction()) {
0474: retVal = handleSimpleAction(pfActionMapping, form,
0475: request, servletContext);
0476: } else {
0477: retVal = getActionMethodForward(actionName,
0478: unwrappedForm, request, response, mapping);
0479: }
0480:
0481: ActionForward ret = forwardTo(retVal, mapping, null,
0482: actionName, null, form, request, response,
0483: servletContext);
0484: long timeTaken = System.currentTimeMillis() - startTime;
0485: eventReporter.actionSuccess(requestContext, this , mapping,
0486: form, ret, timeTaken);
0487: return ret;
0488: } catch (Exception e) {
0489: //
0490: // Even though we handle any Throwable thrown by the user's action method, we don't need
0491: // to catch Throwable here, because anything thrown by the action method will be wrapped
0492: // in an InvocationTargetException. Any Error (or other Throwable) that appears here
0493: // should not be handled by handleException() -- it's probably a framework problem and
0494: // should bubble out to the container.
0495: //
0496: return handleException(e, mapping, form, request, response);
0497: } finally {
0498: try {
0499: ActionForward overrideReturn = null;
0500:
0501: if (gotPastBeforeAction) {
0502: //
0503: // afterAction callback
0504: //
0505: try {
0506: afterAction();
0507: } catch (Throwable th) {
0508: overrideReturn = handleException(th, mapping,
0509: form, request, response);
0510: }
0511: }
0512:
0513: //
0514: // Store information on this action for use with navigateTo=Jpf.NavigateTo.previousAction.
0515: //
0516: savePreviousActionInfo(form, request, mapping,
0517: getServletContext());
0518:
0519: if (overrideReturn != null) {
0520: return overrideReturn;
0521: }
0522: } finally {
0523: setPerRequestState(prevState);
0524: }
0525: }
0526: }
0527:
0528: ActionForward forwardTo(ActionForward fwd, ActionMapping mapping,
0529: PageFlowExceptionConfig exceptionConfig, String actionName,
0530: ModuleConfig altModuleConfig, ActionForm form,
0531: HttpServletRequest request, HttpServletResponse response,
0532: ServletContext servletContext) {
0533: // This method is overridden in PageFlowController. Even though we're just delegating here, we can't remove it.
0534: ActionForwardHandler handler = Handlers.get(servletContext)
0535: .getActionForwardHandler();
0536: FlowControllerHandlerContext context = new FlowControllerHandlerContext(
0537: request, response, this );
0538: return handler.processForward(context, fwd, mapping,
0539: exceptionConfig, actionName, altModuleConfig, form);
0540: }
0541:
0542: NotLoggedInException createNotLoggedInException(String actionName,
0543: HttpServletRequest request) {
0544: if (InternalUtils.sessionExpired(request)) {
0545: return new LoginExpiredException(actionName, this );
0546: } else {
0547: return new NotLoggedInException(actionName, this );
0548: }
0549: }
0550:
0551: /**
0552: * Initialize after object creation. This is a framework-invoked method; it should not normally be called directly.
0553: */
0554: public synchronized void create(HttpServletRequest request,
0555: HttpServletResponse response, ServletContext servletContext) {
0556: PerRequestState prevState = setPerRequestState(new PerRequestState(
0557: request, response, null));
0558:
0559: try {
0560: try {
0561: // we start the context on the bean so user control can run against Controls in the onCreate() method
0562: super .create(request, response, servletContext);
0563: } catch (Throwable th) {
0564: try {
0565: _log.info(
0566: "Handling exception in onCreate(), FlowController "
0567: + this , th);
0568: reinitialize(request, response, servletContext);
0569: ActionForward fwd = handleException(th, null, null,
0570: request, response);
0571: if (fwd == null)
0572: fwd = NULL_ACTION_FORWARD;
0573: request.setAttribute(ONCREATE_EXCEPTION_FORWARD,
0574: fwd);
0575: } catch (Exception e) {
0576: _log.error(
0577: "Exception thrown while handling exception in onCreate(): "
0578: + e.getMessage(), th);
0579: }
0580: }
0581: } finally {
0582: setPerRequestState(prevState);
0583:
0584: PageFlowControlContainer pfcc = PageFlowControlContainerFactory
0585: .getControlContainer(request, getServletContext());
0586: pfcc.endContextOnPageFlow(this );
0587: }
0588:
0589: PageFlowEventReporter er = AdapterManager
0590: .getServletContainerAdapter(servletContext)
0591: .getEventReporter();
0592: RequestContext requestContext = new RequestContext(request,
0593: response);
0594: er.flowControllerCreated(requestContext, this );
0595: }
0596:
0597: /**
0598: * Internal destroy method that is invoked when this object is being removed from the session. This is a
0599: * framework-invoked method; it should not normally be called directly.
0600: */
0601: void destroy(HttpSession session) {
0602: onDestroy(); // for backwards compatiblity
0603: super .destroy(session);
0604:
0605: //
0606: // We may have lost our transient ServletContext reference. Try to get the ServletContext reference from the
0607: // HttpSession object if necessary.
0608: //
0609: ServletContext servletContext = getServletContext();
0610: if (servletContext == null && session != null)
0611: servletContext = session.getServletContext();
0612:
0613: if (servletContext != null) {
0614: PageFlowEventReporter er = AdapterManager
0615: .getServletContainerAdapter(servletContext)
0616: .getEventReporter();
0617: er.flowControllerDestroyed(this , session);
0618: }
0619: }
0620:
0621: /**
0622: * Get the Struts module path for this controller.
0623: *
0624: * @return a String that is the Struts module path for this controller.
0625: */
0626: public abstract String getModulePath();
0627:
0628: /**
0629: * Callback that occurs before any user action method is invoked. {@link FlowController#getRequest},
0630: * {@link FlowController#getResponse}, {@link FlowController#getSession}, and
0631: * {@link FlowController#getActionMapping} may all be used during this method. The action to be run can be
0632: * discovered by calling {@link ActionMapping#getPath} on the value returned from
0633: * {@link FlowController#getActionMapping}.
0634: */
0635: protected synchronized void beforeAction() throws Exception {
0636: }
0637:
0638: /**
0639: * Callback that occurs after any user action method is invoked. {@link FlowController#getRequest},
0640: * {@link FlowController#getResponse}, {@link FlowController#getSession}, and
0641: * {@link FlowController#getActionMapping} may all be used during this method. The action that was run can be
0642: * discovered by calling {@link ActionMapping#getPath} on the value returned from
0643: * {@link FlowController#getActionMapping}.
0644: */
0645: protected synchronized void afterAction() throws Exception {
0646: }
0647:
0648: /**
0649: * Callback that is invoked when this controller instance is created. {@link FlowController#getRequest},
0650: * {@link FlowController#getResponse}, {@link FlowController#getSession} may all be used during this method.
0651: */
0652: protected void onCreate() throws Exception {
0653: }
0654:
0655: /**
0656: * Callback that is invoked when this controller instance is "destroyed", i.e., removed from the user session.
0657: * {@link FlowController#getRequest}, {@link FlowController#getResponse}, and {@link FlowController#getActionMapping}
0658: * may <i>not</i> be used during this method, since it may be called due to session termination outside of a
0659: * request. {@link FlowController#getSession} also may not be used, but the session is passed as an argument
0660: * to {@link FlowController#onDestroy(HttpSession)}, which should be used in place of this method.
0661: * <br>
0662: * Note that this method is <strong>not synchronized</strong>. It is dangerous to synchronize your override of
0663: * this method because it is invoked during a callback from the Servlet container. Depending on the container,
0664: * synchronization here can cause deadlocks.
0665: *
0666: * @deprecated {@link FlowController#onDestroy(HttpSession)} should be used instead.
0667: */
0668: protected void onDestroy() {
0669: }
0670:
0671: /**
0672: * Callback that is invoked when this controller instance is "destroyed", i.e., removed from the user session.
0673: * {@link FlowController#getRequest}, {@link FlowController#getResponse}, and {@link FlowController#getActionMapping}
0674: * may <i>not</i> be used during this method, since it may be called due to session termination outside of a
0675: * request. {@link FlowController#getSession} also may not be used, but the session is passed as an argument.
0676: * <br>
0677: * Note that this method is <strong>not synchronized</strong>. It is dangerous to synchronize your override of
0678: * this method because it is invoked during a callback from the Servlet container. Depending on the container,
0679: * synchronization here can cause deadlocks.
0680: */
0681: protected void onDestroy(HttpSession session) {
0682: }
0683:
0684: /**
0685: * Get a legacy PreviousPageInfo.
0686: * @deprecated This method will be removed without replacement in a future release.
0687: */
0688: public abstract PreviousPageInfo getPreviousPageInfoLegacy(
0689: PageFlowController curJpf, HttpServletRequest request);
0690:
0691: /**
0692: * Get an action handler method of the given name/signature.
0693: *
0694: * @param methodName the name of the action handler method to query.
0695: * @param argType the type of the argument to the action handler method; if <code>null</code>,
0696: * the method takes no arguments.
0697: * @return the desired Method, or <code>null</code> if it doesn't exist.
0698: */
0699: protected Method getActionMethod(String methodName, Class argType) {
0700: String cacheKey = argType != null ? methodName + '/'
0701: + argType.getName() : methodName;
0702: Class this Class = getClass();
0703: ClassLevelCache cache = ClassLevelCache.getCache(this Class);
0704: Method actionMethod = (Method) cache.get(
0705: CACHEID_ACTION_METHODS, cacheKey);
0706:
0707: if (actionMethod != null) {
0708: return actionMethod;
0709: } else {
0710: //
0711: // We didn't find it in the cache. Look for it reflectively.
0712: //
0713: if (argType == null) {
0714: //
0715: // No form -- look for a method with no arguments.
0716: //
0717: actionMethod = InternalUtils.lookupMethod(this Class,
0718: methodName, null);
0719: } else {
0720: //
0721: // Has a form. Look for a method with a single argument -- either the given type
0722: // or any superclass.
0723: //
0724: while (argType != null) {
0725: actionMethod = InternalUtils.lookupMethod(
0726: this Class, methodName,
0727: new Class[] { argType });
0728:
0729: if (actionMethod != null) {
0730: break;
0731: }
0732:
0733: argType = argType.getSuperclass();
0734: }
0735: }
0736:
0737: if (actionMethod != null
0738: && actionMethod.getReturnType().equals(
0739: Forward.class)) {
0740: if (!Modifier.isPublic(actionMethod.getModifiers()))
0741: actionMethod.setAccessible(true);
0742: cache.put(CACHEID_ACTION_METHODS, cacheKey,
0743: actionMethod);
0744: return actionMethod;
0745: }
0746: }
0747:
0748: return null;
0749: }
0750:
0751: private Class getFormClass(Object form, ActionMapping mapping,
0752: HttpServletRequest request) throws ClassNotFoundException {
0753: if (mapping instanceof PageFlowActionMapping) {
0754: String formClassName = ((PageFlowActionMapping) mapping)
0755: .getFormClass();
0756:
0757: if (formClassName != null) {
0758: return InternalUtils.getReloadableClass(formClassName,
0759: getServletContext());
0760: }
0761: }
0762:
0763: return form != null ? form.getClass() : null;
0764: }
0765:
0766: /**
0767: * Get the ActionForward returned by the action handler method that corresponds to the
0768: * given action name and form-bean, or send an error to the browser if there is no
0769: * matching method.
0770: *
0771: * @param actionName the name of the Struts action to handle.
0772: * @param inputForm the form-bean associated with the action. May be <code>null</code>.
0773: * @param response the current HttpServletResponse.
0774: * @return the ActionForward returned by the action handler method, or <code>null</code> if
0775: * there was no matching method (in which case an error was written to the
0776: * browser.
0777: * @throws Exception if an Exception was raised in user code.
0778: */
0779: ActionForward getActionMethodForward(String actionName,
0780: Object inputForm, HttpServletRequest request,
0781: HttpServletResponse response, ActionMapping mapping)
0782: throws Exception {
0783: //
0784: // Find the method.
0785: //
0786: Class formClass = getFormClass(inputForm, mapping, request);
0787: Method actionMethod = getActionMethod(actionName, formClass);
0788:
0789: //
0790: // Invoke the method.
0791: //
0792: if (actionMethod != null) {
0793: return invokeActionMethod(actionMethod, inputForm, request,
0794: mapping);
0795: }
0796:
0797: if (_log.isWarnEnabled()) {
0798: InternalStringBuilder msg = new InternalStringBuilder(
0799: "Could not find matching action method for action=");
0800: msg.append(actionName).append(", form=");
0801: msg.append(inputForm != null ? inputForm.getClass()
0802: .getName() : "[none]");
0803: _log.warn(msg.toString());
0804: }
0805:
0806: PageFlowException ex = new NoMatchingActionMethodException(
0807: actionName, inputForm, this );
0808: InternalUtils.throwPageFlowException(ex, request);
0809: return null;
0810: }
0811:
0812: private static String getFormQualifiedActionPath(Class formClass,
0813: String actionPath) {
0814: InternalStringBuilder ret = new InternalStringBuilder(
0815: actionPath);
0816: ret.append('_');
0817: ret.append(formClass.getName().replace('.', '_').replace('$',
0818: '_'));
0819: return ret.toString();
0820: }
0821:
0822: /**
0823: * Invoke the given action handler method, passing it an argument if appropriate.
0824: *
0825: * @param method the action handler method to invoke.
0826: * @param arg the form-bean to pass; may be <code>null</code>.
0827: * @return the ActionForward returned by the action handler method.
0828: * @throws Exception if an Exception was raised in user code.
0829: */
0830: protected ActionForward invokeActionMethod(Method method, Object arg)
0831: throws Exception {
0832: return invokeActionMethod(method, arg, getRequest(),
0833: getActionMapping());
0834: }
0835:
0836: /**
0837: * Invoke the given action handler method, passing it an argument if appropriate.
0838: *
0839: * @param method the action handler method to invoke.
0840: * @param arg the form-bean to pass; may be <code>null</code>.
0841: * @param request the current HttpServletRequest.
0842: * @return the ActionForward returned by the action handler method.
0843: * @throws Exception if an Exception was raised in user code.
0844: */
0845: ActionForward invokeActionMethod(Method method, Object arg,
0846: HttpServletRequest request, ActionMapping mapping)
0847: throws Exception {
0848: Class[] paramTypes = method.getParameterTypes();
0849:
0850: try {
0851: if (paramTypes.length > 0 && paramTypes[0].isInstance(arg)) {
0852: if (_log.isDebugEnabled()) {
0853: _log.debug("Invoking action method "
0854: + method.getName() + '('
0855: + paramTypes[0].getName() + ')');
0856: }
0857:
0858: return (ActionForward) method.invoke(this ,
0859: new Object[] { arg });
0860: } else if (paramTypes.length == 0) {
0861: if (_log.isDebugEnabled()) {
0862: _log.debug("Invoking action method "
0863: + method.getName() + "()");
0864: }
0865:
0866: return (ActionForward) method.invoke(this , null);
0867: }
0868: } finally {
0869: boolean readonly = false;
0870:
0871: if (mapping instanceof PageFlowActionMapping) {
0872: PageFlowActionMapping pfam = (PageFlowActionMapping) mapping;
0873: readonly = pfam.isReadonly();
0874: }
0875:
0876: /*
0877: A read only Flow Controller is one that is marked as such via metadata. If a FlowController
0878: is read only, no steps need to be taken to ensure that it fails over into the session for
0879: serialization in a cluster.
0880: */
0881: if (!readonly) {
0882: ensureFailover(getRequest());
0883: }
0884: }
0885:
0886: if (_log.isWarnEnabled()) {
0887: _log
0888: .warn("Could not find action method "
0889: + method.getName()
0890: + " with appropriate signature.");
0891: }
0892:
0893: return null;
0894: }
0895:
0896: /**
0897: * Get the current HttpServletRequest. This call is only valid during {@link FlowController#execute} (where
0898: * any user action method is invoked), and during the lifecycle methods {@link FlowController#onCreate},
0899: * {@link FlowController#beforeAction}, {@link FlowController#afterAction}.
0900: *
0901: * @return the current HttpServletRequest.
0902: * @throws IllegalStateException if this method is invoked outside of action method
0903: * execution (i.e., outside of the call to {@link FlowController#execute}, and outside of
0904: * {@link FlowController#onCreate}, {@link FlowController#beforeAction}, {@link FlowController#afterAction}.
0905: */
0906: protected final HttpServletRequest getRequest() {
0907: if (_perRequestState == null) {
0908: throw new IllegalStateException(
0909: "getRequest was called outside of a valid context.");
0910: }
0911:
0912: return _perRequestState.getRequest();
0913: }
0914:
0915: /**
0916: * Get the current HttpServletResponse. This call is only valid during {@link FlowController#execute} (where
0917: * any user action method is invoked), and during the lifecycle methods {@link FlowController#onCreate},
0918: * {@link FlowController#beforeAction}, {@link FlowController#afterAction}.
0919: *
0920: * @return the current HttpServletResponse.
0921: * @throws IllegalStateException if this method is invoked outside of action method
0922: * execution (i.e., outside of the call to {@link FlowController#execute}, and outside of
0923: * {@link FlowController#onCreate}, {@link FlowController#beforeAction}, {@link FlowController#afterAction}.
0924: */
0925: protected final HttpServletResponse getResponse() {
0926: if (_perRequestState == null) {
0927: throw new IllegalStateException(
0928: "getResponse was called outside of a valid context.");
0929: }
0930:
0931: return _perRequestState.getResponse();
0932: }
0933:
0934: /**
0935: * Get the current Struts ActionMapping, which is information from the Struts-XML <action>
0936: * tag that corresponds to the current action being executed. This call is only valid during
0937: * {@link FlowController#execute} (where any user action method is invoked), and during the lifecycle
0938: * methods {@link FlowController#beforeAction} and {@link FlowController#afterAction}.
0939: * @deprecated Use {@link FlowController#getActionMapping} instead.
0940: *
0941: * @return the current Struts ActionMapping.
0942: * @throws IllegalStateException if this method is invoked outside of action method
0943: * execution (i.e., outside of the call to {@link FlowController#execute}, and outside of
0944: * {@link FlowController#onCreate}, {@link FlowController#beforeAction}, {@link FlowController#afterAction}.
0945: */
0946: protected final ActionMapping getMapping() {
0947: return getActionMapping();
0948: }
0949:
0950: /**
0951: * Get the current Struts ActionMapping, which is information from the Struts-XML <action>
0952: * tag that corresponds to the current action being executed. This call is only valid
0953: * during {@link FlowController#execute} (where any user action method is invoked), and during the lifecycle
0954: * methods {@link FlowController#beforeAction} and {@link FlowController#afterAction}.
0955: *
0956: * @return the current Struts ActionMapping.
0957: * @throws IllegalStateException if this method is invoked outside of action method
0958: * execution (i.e., outside of the call to {@link FlowController#execute}, and outside of
0959: * {@link FlowController#onCreate}, {@link FlowController#beforeAction}, {@link FlowController#afterAction}.
0960: */
0961: protected final ActionMapping getActionMapping() {
0962: if (_perRequestState == null) {
0963: throw new IllegalStateException(
0964: "getActionMapping was called outside of a valid context.");
0965: }
0966:
0967: return _perRequestState.getActionMapping();
0968: }
0969:
0970: /**
0971: * Get the current user session. This call is only valid during {@link FlowController#execute} (where
0972: * any user action method is invoked), and during the lifecycle methods {@link FlowController#onCreate},
0973: * {@link FlowController#onDestroy}, {@link FlowController#beforeAction}, {@link FlowController#afterAction}.
0974: *
0975: * @return the HttpSession for the current user session.
0976: * @throws IllegalStateException if this method is invoked outside of action method
0977: * execution (i.e., outside of the call to {@link FlowController#execute}, and outside of
0978: * {@link FlowController#onCreate}, {@link FlowController#onDestroy}, {@link FlowController#beforeAction}, {@link FlowController#afterAction}.
0979: */
0980: protected final HttpSession getSession() {
0981: if (_perRequestState == null) {
0982: throw new IllegalStateException(
0983: "getSession was called outside of a valid context.");
0984: }
0985:
0986: return _perRequestState.getRequest().getSession(true);
0987: }
0988:
0989: PerRequestState setPerRequestState(PerRequestState state) {
0990: if (state != null) {
0991: assert state.getRequest() != null;
0992: assert state.getResponse() != null;
0993: }
0994:
0995: PerRequestState prevState = _perRequestState;
0996: _perRequestState = state;
0997: return prevState;
0998: }
0999:
1000: /**
1001: * Get the Struts ModuleConfig object associated with this FlowController.
1002: */
1003: protected final ModuleConfig getModuleConfig() {
1004: if (_moduleConfig == null) {
1005: _moduleConfig = InternalUtils.ensureModuleConfig(
1006: getModulePath(), getServletContext());
1007: assert _moduleConfig != null : getModulePath() + "; "
1008: + getClass().getName();
1009: }
1010: return _moduleConfig;
1011: }
1012:
1013: /**
1014: * This is a non-property public accessor that will return the property value <code>moduleConfig</code>.
1015: * This is a non-property method because properties are exposed to databinding and that would expose
1016: * internal data structures to attack.
1017: * @return the ModuleConfig for this FlowController
1018: */
1019: public final ModuleConfig theModuleConfig() {
1020: return getModuleConfig();
1021: }
1022:
1023: /**
1024: * Gets the Struts module configuration associated with this controller.
1025: * @deprecated Use {@link #getModuleConfig()} instead.
1026: *
1027: * @param servletContext the current ServletContext.
1028: * @return the Struts ModuleConfig for this controller.
1029: */
1030: public ModuleConfig getModuleConfig(ServletContext servletContext,
1031: HttpServletRequest request) {
1032: return getModuleConfig();
1033: }
1034:
1035: /**
1036: * Resolve the given action name to a URI. This version assumes that the ActionServlet
1037: * class should be {@link PageFlowActionServlet}.
1038: * Note: this method invokes the full action-processing cycle on a {@link ScopedRequest}. Use
1039: * {@link FlowController#resolveAction} to resolve the URI for an action in the current page flow.
1040: * @deprecated Use {@link PageFlowUtils#strutsLookup} instead. This method will be removed in v1.1.
1041: */
1042: public static ActionResult lookup(String actionName,
1043: ServletContext context, HttpServletRequest request,
1044: HttpServletResponse response) throws Exception {
1045: return PageFlowUtils.strutsLookup(context, request, response,
1046: actionName, null);
1047: }
1048:
1049: /**
1050: * Resolve the given action name to a URI.
1051: * Note: this method invokes the full action-processing cycle on a {@link ScopedRequest}. Use
1052: * {@link FlowController#resolveAction} to resolve the URI for an action in the current page flow.
1053: * @deprecated Use {@link PageFlowUtils#strutsLookup} instead. This method will be removed in v1.1.
1054: */
1055: public static ActionResult lookup(String actionName,
1056: ServletContext context, HttpServletRequest request,
1057: HttpServletResponse response, String actionServletClassName)
1058: throws Exception {
1059: return PageFlowUtils.strutsLookup(context, request, response,
1060: actionName, null);
1061: }
1062:
1063: /**
1064: * Call an action and return the result URI.
1065: *
1066: * @param actionName the name of the action to run.
1067: * @param form the form bean instance to pass to the action, or <code>null</code> if none should be passed.
1068: * @return the result webapp-relative URI, as a String.
1069: * @throws ActionNotFoundException when the given action does not exist in this FlowController.
1070: * @throws Exception if the action method throws an Exception.
1071: */
1072: public String resolveAction(String actionName, Object form,
1073: HttpServletRequest request, HttpServletResponse response)
1074: throws Exception {
1075: ActionMapping mapping = (ActionMapping) getModuleConfig()
1076: .findActionConfig('/' + actionName);
1077:
1078: if (mapping == null) {
1079: InternalUtils
1080: .throwPageFlowException(
1081: new ActionNotFoundException(actionName,
1082: this , form), request);
1083: }
1084:
1085: ActionForward fwd = getActionMethodForward(actionName, form,
1086: request, response, mapping);
1087:
1088: if (fwd instanceof Forward) {
1089: ((Forward) fwd).initialize(mapping, this , request);
1090: }
1091:
1092: String path = fwd.getPath();
1093: if (fwd.getContextRelative() || FileUtils.isAbsoluteURI(path)) {
1094: return path;
1095: } else {
1096: return getModulePath() + path;
1097: }
1098: }
1099:
1100: /**
1101: * Call an action and return the result URI.
1102: *
1103: * @deprecated Use {@link FlowController#resolveAction(String, Object, HttpServletRequest, HttpServletResponse)} instead.
1104: * @param actionName the name of the action to run.
1105: * @param form the form bean instance to pass to the action, or <code>null</code> if none should be passed.
1106: * @return the result webapp-relative URI, as a String.
1107: * @throws ActionNotFoundException when the given action does not exist in this FlowController.
1108: * @throws Exception if the action method throws an Exception.
1109: */
1110: public String resolveAction(String actionName, Object form)
1111: throws Exception {
1112: return resolveAction(actionName, form, getRequest(),
1113: getResponse());
1114: }
1115:
1116: /**
1117: * Get a list of the names of actions handled by methods in this PageFlowController.
1118: *
1119: * @return a String array containing the names of actions handled by methods in this PageFlowController.
1120: */
1121: protected String[] getActions() {
1122: ActionConfig[] actionConfigs = getModuleConfig()
1123: .findActionConfigs();
1124: ArrayList actionNames = new ArrayList();
1125:
1126: for (int i = 0; i < actionConfigs.length; i++) {
1127: ActionConfig ac = actionConfigs[i];
1128: actionNames.add(ac.getPath().substring(1)); // every action path has a '/' in front of it
1129: }
1130:
1131: return (String[]) actionNames.toArray(new String[0]);
1132: }
1133:
1134: /**
1135: * Tell whether a given String is the name of an action handled by a method in this PageFlowController.
1136: *
1137: * @param name the action-name to query.
1138: * @return <code>true</code> if <code>name</code> is the name of an action handled by a method in this
1139: * PageFlowController.
1140: */
1141: public boolean isAction(String name) {
1142: return getModuleConfig().findActionConfig('/' + name) != null;
1143: }
1144:
1145: /**
1146: * Tell whether this is a {@link PageFlowController}.
1147: *
1148: * @return <code>true</code> if this is a {@link PageFlowController}.
1149: */
1150: public boolean isPageFlow() {
1151: return false;
1152: }
1153:
1154: /**
1155: * Get the current Struts ActionServlet.
1156: *
1157: * @deprecated This method will be removed with no replacement. In most cases, {@link FlowController#getServletContext()} is
1158: * sufficient; for other cases, the ActionServlet itself is in the ServletContext attribute
1159: * {@link Globals#ACTION_SERVLET_KEY}.
1160: * @return the ActionServlet.
1161: */
1162: protected ActionServlet getServlet() {
1163: return InternalUtils.getActionServlet(getServletContext());
1164: }
1165:
1166: /**
1167: * Called on this object for non-lookup (refresh) requests. This is a framework-invoked method that should not
1168: * normally be called directly.
1169: */
1170: public final synchronized void refresh(HttpServletRequest request,
1171: HttpServletResponse response) {
1172: PerRequestState prevState = setPerRequestState(new PerRequestState(
1173: request, response, null));
1174:
1175: try {
1176: onRefresh();
1177: } finally {
1178: setPerRequestState(prevState);
1179: }
1180: }
1181:
1182: /**
1183: * Callback that is invoked when this controller is involved in a refresh request, as can happen in a portal
1184: * environment on a request where no action is run in the current page flow, but a previously-displayed page in the
1185: * page flow is re-rendered.
1186: */
1187: protected void onRefresh() {
1188: }
1189:
1190: /**
1191: * Remove this instance from the user session.
1192: */
1193: protected void remove() {
1194: removeFromSession(getRequest());
1195: }
1196:
1197: /**
1198: * Used by derived classes to store information on the most recent action executed.
1199: */
1200: void savePreviousActionInfo(ActionForm form,
1201: HttpServletRequest request, ActionMapping mapping,
1202: ServletContext servletContext) {
1203: }
1204:
1205: /**
1206: * Store information about recent pages displayed. This is a framework-invoked method that should not normally be
1207: * called directly.
1208: */
1209: public void savePreviousPageInfo(ActionForward forward,
1210: ActionForm form, ActionMapping mapping,
1211: HttpServletRequest request, ServletContext servletContext,
1212: boolean isSpecialForward) {
1213: }
1214:
1215: /**
1216: * When this FlowController does not use a {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward @Jpf.Forward}
1217: * annotation with a
1218: * <code>navigateTo=</code>{@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#previousAction Jpf.NavigateTo.previousAction}
1219: * attribute, the following methods always return <code>null</code> by default.
1220: * <ul>
1221: * <li>getPreviousActionInfo</li>
1222: * <li>getPreviousActionURI</li>
1223: * <li>getPreviousForm</li>
1224: * </ul>
1225: * Override <code>alwaysTrackPreviousAction</code> (which always returns <code>false</code>) to enable these methods
1226: * in all cases.
1227: *
1228: * @return <code>true</code> if the previous action should always be tracked, regardless of whether
1229: * <code>return-to="previousAction"</code> is used.
1230: * @see PageFlowController#getPreviousActionInfo
1231: * @see PageFlowController#getPreviousActionURI
1232: * @see PageFlowController#getPreviousFormBean
1233: */
1234: protected boolean alwaysTrackPreviousAction() {
1235: return false;
1236: }
1237:
1238: /**
1239: * When this FlowController does not use a {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward @Jpf.Forward}
1240: * annotation with either a
1241: * <code>navigateTo</code>={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#currentPage Jpf.NavigateTo.currentPage}
1242: * attribute or a
1243: * <code>navigateTo</code>={@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#previousPage Jpf.NavigateTo.previousPage}
1244: * attribute, the following methods always return <code>null</code> by default.
1245: * <ul>
1246: * <li>getCurrentPageInfo</li>
1247: * <li>getPreviousPageInfo</li>
1248: * <li>getCurrentForwardPath</li>
1249: * <li>getPreviousForwardPath</li>
1250: * </ul>
1251: * Override <code>alwaysTrackPreviousPage</code> (which always returns <code>false</code>) to enable these methods
1252: * in all cases.
1253: *
1254: * @return <code>true</code> if the previous page should always be tracked, regardless
1255: * of whether <code>return-to="currentPage"</code> or <code>return-to="previousPage"</code>
1256: * is used.
1257: * @see PageFlowController#getCurrentPageInfo
1258: * @see PageFlowController#getPreviousPageInfo
1259: * @see PageFlowController#getCurrentForwardPath
1260: * @see PageFlowController#getPreviousForwardPath
1261: */
1262: protected boolean alwaysTrackPreviousPage() {
1263: return false;
1264: }
1265:
1266: /**
1267: * Increment the count of concurrent requests to this FlowController. Note that this method
1268: * is not synchronized -- we use it to decide whether to synchronize on this instance,
1269: * or to bail out with an error message about too many concurrent requests.
1270: */
1271: boolean incrementRequestCount(HttpServletRequest request,
1272: HttpServletResponse response, ServletContext servletContext)
1273: throws IOException {
1274: //
1275: // First cache the max-concurrent-request-count value.
1276: //
1277: if (_maxConcurrentRequestCount == -1) {
1278: _maxConcurrentRequestCount = DEFAULT_MAX_CONCURRENT_REQUEST_COUNT;
1279: assert servletContext != null;
1280:
1281: String countStr = servletContext
1282: .getInitParameter(MAX_CONCURRENT_REQUESTS_PARAM);
1283: if (countStr != null) {
1284: try {
1285: _maxConcurrentRequestCount = Integer
1286: .parseInt(countStr);
1287: } catch (NumberFormatException e) {
1288: _log.error(
1289: "Invalid value for servlet context parameter"
1290: + MAX_CONCURRENT_REQUESTS_PARAM
1291: + ": " + countStr, e);
1292: }
1293:
1294: if (_maxConcurrentRequestCount < 1) {
1295: _maxConcurrentRequestCount = DEFAULT_MAX_CONCURRENT_REQUEST_COUNT;
1296: _log
1297: .error("Invalid value for servlet context parameter"
1298: + MAX_CONCURRENT_REQUESTS_PARAM
1299: + ": " + countStr);
1300: }
1301: }
1302: }
1303:
1304: //
1305: // Now, if the current count of concurrent requests to this instance is greater than the max,
1306: // send an error on the response.
1307: //
1308: if (_requestCount >= _maxConcurrentRequestCount) {
1309: if (_log.isInfoEnabled()) {
1310: _log.info("Too many requests to FlowController "
1311: + getDisplayName() + " (" + (_requestCount + 1)
1312: + '>' + _maxConcurrentRequestCount
1313: + "); returning error code "
1314: + EXCEEDED_MAX_CONCURRENT_REQUESTS_ERRORCODE);
1315: }
1316:
1317: response
1318: .sendError(EXCEEDED_MAX_CONCURRENT_REQUESTS_ERRORCODE);
1319: return false;
1320: }
1321:
1322: //
1323: // We're ok -- increment the count and continue.
1324: //
1325: ++_requestCount;
1326: return true;
1327: }
1328:
1329: void decrementRequestCount(HttpServletRequest request) {
1330: assert _requestCount > 0 : request.getRequestURI();
1331: --_requestCount;
1332: }
1333:
1334: /**
1335: * Invoke the given exception handler method. This is a framework-invoked method that should not normally be called
1336: * directly
1337: *
1338: * @param method the action handler method to invoke.
1339: * @param ex the Throwable that is to be handled.
1340: * @param message the String message that is to be passed to the handler method.
1341: * @param wrappedFormBean the wrapped form bean that is associated with the action being processed; may be <code>null</code>.
1342: * @param exceptionConfig the exception configuration object for the current exception handler.
1343: * @param actionMapping the action configuration object for the requested action.
1344: * @param request the current HttpServletRequest.
1345: * @param response the current HttpServletResponse.
1346: * @return the ActionForward returned by the exception handler method.
1347: */
1348: public synchronized ActionForward invokeExceptionHandler(
1349: Method method, Throwable ex, String message,
1350: ActionForm wrappedFormBean,
1351: PageFlowExceptionConfig exceptionConfig,
1352: ActionMapping actionMapping, HttpServletRequest request,
1353: HttpServletResponse response) throws IOException,
1354: ServletException {
1355: PerRequestState prevState = setPerRequestState(new PerRequestState(
1356: request, response, actionMapping));
1357:
1358: try {
1359: if (_log.isDebugEnabled()) {
1360: _log.debug("Invoking exception handler method "
1361: + method.getName() + '('
1362: + method.getParameterTypes()[0].getName()
1363: + ", ...)");
1364: }
1365:
1366: try {
1367: ActionForward retVal = null;
1368: String actionName = InternalUtils
1369: .getActionName(actionMapping);
1370:
1371: if (actionName == null
1372: && ex instanceof PageFlowException) {
1373: actionName = ((PageFlowException) ex)
1374: .getActionName();
1375: }
1376:
1377: try {
1378: Object formBean = InternalUtils
1379: .unwrapFormBean(wrappedFormBean);
1380: Object[] args = new Object[] { ex, actionName,
1381: message, formBean };
1382: retVal = (ActionForward) method.invoke(this , args);
1383: } finally {
1384: if (!exceptionConfig.isReadonly()) {
1385: ensureFailover(request);
1386: }
1387: }
1388:
1389: return retVal;
1390: } catch (InvocationTargetException e) {
1391: Throwable target = e.getTargetException();
1392:
1393: if (target instanceof Exception) {
1394: throw (Exception) target;
1395: } else {
1396: throw e;
1397: }
1398: }
1399: } catch (Throwable e) {
1400: _log.error("Exception while handling exception "
1401: + ex.getClass().getName()
1402: + ". The original exception will be thrown.", e);
1403:
1404: ExceptionsHandler eh = Handlers.get(getServletContext())
1405: .getExceptionsHandler();
1406: FlowControllerHandlerContext context = new FlowControllerHandlerContext(
1407: request, response, this );
1408: Throwable unwrapped = eh.unwrapException(context, e);
1409:
1410: if (!eh.eatUnhandledException(context, unwrapped)) {
1411: if (ex instanceof ServletException)
1412: throw (ServletException) ex;
1413: if (ex instanceof IOException)
1414: throw (IOException) ex;
1415: if (ex instanceof Error)
1416: throw (Error) ex;
1417: throw new UnhandledException(ex);
1418: }
1419:
1420: return null;
1421: } finally {
1422: setPerRequestState(prevState);
1423: }
1424: }
1425:
1426: /**
1427: * Add a property-related message that will be shown with the Errors and Error tags.
1428: *
1429: * @param propertyName the name of the property with which to associate this error.
1430: * @param messageKey the message-resources key for the message.
1431: * @param messageArgs zero or more arguments to the message.
1432: */
1433: protected void addActionError(String propertyName,
1434: String messageKey, Object[] messageArgs) {
1435: InternalUtils.addActionError(propertyName, new ActionMessage(
1436: messageKey, messageArgs), getRequest());
1437: }
1438:
1439: /**
1440: * Add a property-related message as an expression that will be evaluated and shown with the Errors and Error tags.
1441: *
1442: * @param propertyName the name of the property with which to associate this error.
1443: * @param expression the expression that will be evaluated to generate the error message.
1444: * @param messageArgs zero or more arguments to the message; may be expressions.
1445: */
1446: protected void addActionErrorExpression(String propertyName,
1447: String expression, Object[] messageArgs) {
1448: PageFlowUtils.addActionErrorExpression(getRequest(),
1449: propertyName, expression, messageArgs);
1450: }
1451:
1452: /**
1453: * Add a validation error that will be shown with the Errors and Error tags.
1454: * @deprecated Use {@link #addActionError} instead.
1455: *
1456: * @param propertyName the name of the property with which to associate this error.
1457: * @param messageKey the message-resources key for the error message.
1458: * @param messageArgs an array of arguments for the error message.
1459: */
1460: protected void addValidationError(String propertyName,
1461: String messageKey, Object[] messageArgs) {
1462: PageFlowUtils.addValidationError(propertyName, messageKey,
1463: messageArgs, getRequest());
1464: }
1465:
1466: /**
1467: * Add a validation error that will be shown with the Errors and Error tags.
1468: * @deprecated Use {@link #addActionError} instead.
1469: *
1470: * @param propertyName the name of the property with which to associate this error.
1471: * @param messageKey the message-resources key for the error message.
1472: */
1473: protected void addValidationError(String propertyName,
1474: String messageKey) {
1475: PageFlowUtils.addValidationError(propertyName, messageKey,
1476: getRequest());
1477: }
1478:
1479: private static ActionForward handleSimpleAction(
1480: PageFlowActionMapping mapping, ActionForm wrappedFormBean,
1481: HttpServletRequest request, ServletContext servletContext) {
1482:
1483: Map/*< String, String >*/conditionalForwards = mapping
1484: .getConditionalForwardsMap();
1485:
1486: if (!conditionalForwards.isEmpty()) {
1487: Object formBean = InternalUtils
1488: .unwrapFormBean(wrappedFormBean);
1489:
1490: for (Iterator/*< Map.Entry< String, String > >*/i = conditionalForwards
1491: .entrySet().iterator(); i.hasNext();) {
1492: Map.Entry/*< String, String >*/entry = (Map.Entry) i
1493: .next();
1494: String expression = (String) entry.getKey();
1495: String forwardName = (String) entry.getValue();
1496:
1497: try {
1498: if (InternalExpressionUtils.evaluateCondition(
1499: expression, formBean, request,
1500: servletContext)) {
1501: if (_log.isTraceEnabled()) {
1502: _log
1503: .trace("Expression '"
1504: + expression
1505: + "' evaluated to true on simple action "
1506: + mapping.getPath()
1507: + "; using forward "
1508: + forwardName + '.');
1509: }
1510:
1511: return new Forward(forwardName);
1512: }
1513: } catch (Exception e) // ELException
1514: {
1515: if (_log.isErrorEnabled()) {
1516: _log.error(
1517: "Exception occurred evaluating navigation expression '"
1518: + expression + "'. Cause: "
1519: + e.getCause(), e);
1520: }
1521: }
1522: }
1523: }
1524:
1525: String defaultForwardName = mapping.getDefaultForward();
1526: assert defaultForwardName != null : "defaultForwardName is null on Simple Action "
1527: + mapping.getPath();
1528:
1529: if (_log.isTraceEnabled()) {
1530: _log
1531: .trace("No expression evaluated to true on simple action "
1532: + mapping.getPath()
1533: + "; using forward "
1534: + defaultForwardName + '.');
1535: }
1536:
1537: return new Forward(defaultForwardName);
1538: }
1539:
1540: /**
1541: * Get the system default locale.
1542: *
1543: * @return the system default locale.
1544: */
1545: protected static Locale getDefaultLocale() {
1546: return defaultLocale;
1547: }
1548:
1549: /**
1550: * Return the default data source for the Struts module associated with this FlowController.
1551: *
1552: * @param request the current request.
1553: */
1554: protected DataSource getDataSource(HttpServletRequest request) {
1555: return getDataSource(request, Globals.DATA_SOURCE_KEY);
1556: }
1557:
1558: /**
1559: * Return the specified data source for the current Struts module.
1560: *
1561: * @param request The servlet request we are processing
1562: * @param key The key specified in the
1563: * <code><message-resources></code> element for the
1564: * requested bundle
1565: */
1566: protected DataSource getDataSource(HttpServletRequest request,
1567: String key) {
1568: // Return the requested data source instance
1569: return (DataSource) getServletContext().getAttribute(
1570: key + getModuleConfig().getPrefix());
1571: }
1572:
1573: /**
1574: * Return the user's currently selected Locale.
1575: *
1576: * @param request the current request.
1577: * @return the user's currently selected Locale, stored in the session.
1578: * @deprecated Use {@link #getLocale} or {@link #retrieveUserLocale}.
1579: */
1580: protected Locale getLocale(HttpServletRequest request) {
1581:
1582: HttpSession session = request.getSession();
1583: Locale locale = (Locale) session
1584: .getAttribute(Globals.LOCALE_KEY);
1585: return locale != null ? locale : DEFAULT_LOCALE;
1586: }
1587:
1588: /**
1589: * Get the user's currently selected Locale.
1590: *
1591: * @return the user's currently selected Locale, stored in the session.
1592: */
1593: protected Locale getLocale() {
1594: return retrieveUserLocale(getRequest(), null);
1595: }
1596:
1597: public static Locale retrieveUserLocale(HttpServletRequest request,
1598: String locale) {
1599: if (locale == null)
1600: locale = Globals.LOCALE_KEY;
1601: HttpSession session = request.getSession(false);
1602: Locale userLocale = null;
1603: if (session != null)
1604: userLocale = (Locale) session.getAttribute(locale);
1605: if (userLocale == null)
1606: userLocale = DEFAULT_LOCALE;
1607: return userLocale;
1608: }
1609:
1610: /**
1611: * Return the message resources for the default module.
1612: *
1613: * @deprecated This method can only return the resources for the default
1614: * module. Use {@link #getMessageResources()} to get the
1615: * resources for this FlowController.
1616: */
1617: protected MessageResources getResources() {
1618: return (MessageResources) getServletContext().getAttribute(
1619: Globals.MESSAGES_KEY);
1620: }
1621:
1622: /**
1623: * Return the default message resources for the current module.
1624: * @deprecated Use {@link #getMessageResources()} instead.
1625: *
1626: * @param request The servlet request we are processing
1627: */
1628: protected MessageResources getResources(HttpServletRequest request) {
1629: return getMessageResources();
1630: }
1631:
1632: /**
1633: * Return the specified message resources for the current module.
1634: * @deprecated Use {@link #getMessageResources(String)} instead.
1635: *
1636: * @param request the current request.
1637: * @param key The bundle key specified in a
1638: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.MessageBundle @Jpf.MessageBundle} annotation.
1639: */
1640: protected MessageResources getResources(HttpServletRequest request,
1641: String key) {
1642: return getMessageResources(key);
1643: }
1644:
1645: /**
1646: * Get the default message resources for this FlowController.
1647: */
1648: protected MessageResources getMessageResources() {
1649: return getMessageResources(Globals.MESSAGES_KEY);
1650: }
1651:
1652: /**
1653: * Get the specified message resources for this FlowController.
1654: *
1655: * @param key The bundle key specified in a
1656: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.MessageBundle @Jpf.MessageBundle} annotation.
1657: */
1658: protected MessageResources getMessageResources(String key) {
1659: return (MessageResources) getServletContext().getAttribute(
1660: key + getModuleConfig().getPrefix());
1661: }
1662:
1663: /**
1664: * <p>Returns <code>true</code> if the current form's cancel button was
1665: * pressed. This method will check if the <code>Globals.CANCEL_KEY</code>
1666: * request attribute has been set, which normally occurs if the cancel
1667: * button generated by the Struts <strong>CancelTag</strong> was pressed by the user
1668: * in the current request. If <code>true</code>, validation performed
1669: * by an <strong>ActionForm</strong>'s <code>validate()</code> method
1670: * will have been skipped by the controller servlet.</p>
1671: * @deprecated This method will be removed without replacement in a future release. The normal method for
1672: * cancelling in a form is to use the <code>action</code> attribute on
1673: * {@link org.apache.beehive.netui.tags.html.Button}, rather than avoiding validation on the current action.
1674: *
1675: * @param request The servlet request we are processing
1676: * @see org.apache.struts.taglib.html.CancelTag
1677: */
1678: protected boolean isCancelled(HttpServletRequest request) {
1679:
1680: return request.getAttribute(Globals.CANCEL_KEY) != null;
1681:
1682: }
1683:
1684: /**
1685: * Generate a new transaction token, to be used for enforcing a single request for a particular transaction.
1686: *
1687: * @param request the current request.
1688: * @deprecated Use {@link #generateToken()} instead.
1689: */
1690: protected String generateToken(HttpServletRequest request) {
1691: return TOKEN_PROCESSOR.generateToken(request);
1692: }
1693:
1694: /**
1695: * Generate a new transaction token, to be used for enforcing a single request for a particular transaction.
1696: *
1697: * @see #isTokenValid()
1698: * @see #isTokenValid(boolean)
1699: * @see #resetToken()
1700: */
1701: protected String generateToken() {
1702: return TOKEN_PROCESSOR.generateToken(getRequest());
1703: }
1704:
1705: /**
1706: * Return <code>true</code> if there is a transaction token stored in
1707: * the user's current session, and the value submitted as a request
1708: * parameter with this action matches it. Returns <code>false</code>
1709: * under any of the following circumstances:
1710: * <ul>
1711: * <li>No session associated with this request</li>
1712: * <li>No transaction token saved in the session</li>
1713: * <li>No transaction token included as a request parameter</li>
1714: * <li>The included transaction token value does not match the
1715: * transaction token in the user's session</li>
1716: * </ul>
1717: * @deprecated Use {@link #isTokenValid()} instead.
1718: *
1719: * @param request The servlet request we are processing
1720: */
1721: protected boolean isTokenValid(HttpServletRequest request) {
1722:
1723: return TOKEN_PROCESSOR.isTokenValid(request, false);
1724:
1725: }
1726:
1727: /**
1728: * Return <code>true</code> if there is a transaction token stored in
1729: * the user's current session, and the value submitted as a request
1730: * parameter with this action matches it. Returns <code>false</code>
1731: * under any of the following circumstances:
1732: * <ul>
1733: * <li>No session associated with this request</li>
1734: * <li>No transaction token saved in the session</li>
1735: * <li>No transaction token included as a request parameter</li>
1736: * <li>The included transaction token value does not match the
1737: * transaction token in the user's session</li>
1738: * </ul>
1739: *
1740: * @see #generateToken()
1741: * @see #resetToken()
1742: */
1743: protected boolean isTokenValid() {
1744:
1745: return TOKEN_PROCESSOR.isTokenValid(getRequest(), false);
1746:
1747: }
1748:
1749: /**
1750: * Return <code>true</code> if there is a transaction token stored in
1751: * the user's current session, and the value submitted as a request
1752: * parameter with this action matches it. Returns <code>false</code>
1753: * <ul>
1754: * <li>No session associated with this request</li>
1755: * <li>No transaction token saved in the session</li>
1756: * <li>No transaction token included as a request parameter</li>
1757: * <li>The included transaction token value does not match the
1758: * transaction token in the user's session</li>
1759: * </ul>
1760: * @deprecated Use {@link #isTokenValid(boolean)} instead.
1761: *
1762: * @param request The servlet request we are processing
1763: * @param reset Should we reset the token after checking it?
1764: */
1765: protected boolean isTokenValid(HttpServletRequest request,
1766: boolean reset) {
1767: return TOKEN_PROCESSOR.isTokenValid(request, reset);
1768: }
1769:
1770: /**
1771: * Return <code>true</code> if there is a transaction token stored in
1772: * the user's current session, and the value submitted as a request
1773: * parameter with this action matches it. Returns <code>false</code>
1774: * <ul>
1775: * <li>No session associated with this request</li>
1776: * <li>No transaction token saved in the session</li>
1777: * <li>No transaction token included as a request parameter</li>
1778: * <li>The included transaction token value does not match the
1779: * transaction token in the user's session</li>
1780: * </ul>
1781: *
1782: * @param reset Should we reset the token after checking it?
1783: * @see #generateToken()
1784: * @see #resetToken()
1785: */
1786: protected boolean isTokenValid(boolean reset) {
1787:
1788: return TOKEN_PROCESSOR.isTokenValid(getRequest(), reset);
1789: }
1790:
1791: /**
1792: * Reset the saved transaction token in the user's session. This
1793: * indicates that transactional token checking will not be needed
1794: * on the next request that is submitted.
1795: * @deprecated Use {@link #resetToken()} instead.
1796: *
1797: * @param request The servlet request we are processing
1798: */
1799: protected void resetToken(HttpServletRequest request) {
1800: TOKEN_PROCESSOR.resetToken(request);
1801: }
1802:
1803: /**
1804: * Reset the saved transaction token in the user's session. This
1805: * indicates that transactional token checking will not be needed
1806: * on the next request that is submitted.
1807: *
1808: * @see #isTokenValid()
1809: * @see #isTokenValid(boolean)
1810: * @see #generateToken()
1811: */
1812: protected void resetToken() {
1813: TOKEN_PROCESSOR.resetToken(getRequest());
1814: }
1815:
1816: /**
1817: * Save the specified messages keys into the request for use by the <netui:error> or <netui:errors> tags.
1818: * @deprecated Use {@link #saveActionErrors} instead.
1819: *
1820: * @param errors the ActionMessages to save in the request.
1821: */
1822: protected void saveErrors(HttpServletRequest request,
1823: ActionMessages errors) {
1824: saveActionErrors(errors);
1825: }
1826:
1827: /**
1828: * Save the specified messages keys into the appropriate request
1829: * attribute for use by the Struts <html:messages> tag (if
1830: * messages="true" is set), if any messages are required. Otherwise,
1831: * ensure that the request attribute is not created.
1832: * @deprecated This method will be removed without replacement in a future release.
1833: *
1834: * @param request The servlet request we are processing
1835: * @param messages Messages object
1836: */
1837: protected void saveMessages(HttpServletRequest request,
1838: ActionMessages messages) {
1839:
1840: // Remove any messages attribute if none are required
1841: if (messages == null || messages.isEmpty()) {
1842: request.removeAttribute(Globals.MESSAGE_KEY);
1843: return;
1844: }
1845:
1846: // Save the messages we need
1847: request.setAttribute(Globals.MESSAGE_KEY, messages);
1848:
1849: }
1850:
1851: /**
1852: * Save the specified messages keys into the request for use by the <netui:error> or <netui:errors> tags.
1853: *
1854: * @param errors the ActionMessages to save in the request.
1855: */
1856: protected void saveActionErrors(ActionMessages errors) {
1857: // Remove any messages attribute if none are required
1858: if (errors == null || errors.isEmpty()) {
1859: getRequest().removeAttribute(Globals.ERROR_KEY);
1860: } else {
1861: getRequest().setAttribute(Globals.ERROR_KEY, errors);
1862: }
1863: }
1864:
1865: /**
1866: * Save a new transaction token in the user's current session, creating
1867: * a new session if necessary.
1868: *
1869: * @param request The servlet request we are processing
1870: */
1871: protected void saveToken(HttpServletRequest request) {
1872: TOKEN_PROCESSOR.saveToken(request);
1873: }
1874:
1875: /**
1876: * Set the user's currently selected Locale.
1877: *
1878: * @param request The request we are processing
1879: * @param locale The user's selected Locale to be set, or null
1880: * to select the server's default Locale
1881: * @deprecated Use {@link #setLocale(Locale)}.
1882: */
1883: protected void setLocale(HttpServletRequest request, Locale locale) {
1884:
1885: HttpSession session = request.getSession();
1886: if (locale == null) {
1887: locale = getDefaultLocale();
1888: }
1889: session.setAttribute(Globals.LOCALE_KEY, locale);
1890:
1891: }
1892:
1893: /**
1894: * Set the user's currently selected Locale.
1895: *
1896: * @param locale The user's selected Locale to be set, or null to select the server's default Locale
1897: */
1898: protected void setLocale(Locale locale) {
1899: if (locale == null)
1900: locale = getDefaultLocale();
1901: getSession().setAttribute(Globals.LOCALE_KEY, locale);
1902: }
1903:
1904: /**
1905: * Get the flow-scoped form bean member associated with the given ActionMapping. This is a framework-invoked
1906: * method that should not normally be called directly.
1907: */
1908: public ActionForm getFormBean(ActionMapping mapping) {
1909: if (mapping instanceof PageFlowActionMapping) {
1910: PageFlowActionMapping pfam = (PageFlowActionMapping) mapping;
1911: String formMember = pfam.getFormMember();
1912: if (formMember == null)
1913: return null;
1914:
1915: Field field = null;
1916: try {
1917: field = getClass().getDeclaredField(formMember);
1918: } catch (NoSuchFieldException e) {
1919: // try finding a non-private field from the class hierarchy
1920: field = InternalUtils.lookupField(getClass(),
1921: formMember);
1922: if (field == null
1923: || Modifier.isPrivate(field.getModifiers())) {
1924: _log.error("Could not find member field "
1925: + formMember + " as the form bean.");
1926: return null;
1927: }
1928: }
1929:
1930: try {
1931: field.setAccessible(true);
1932: return InternalUtils.wrapFormBean(field.get(this ));
1933: } catch (Exception e) {
1934: _log.error("Could not use member field " + formMember
1935: + " as the form bean.", e);
1936: }
1937: }
1938:
1939: return null;
1940: }
1941:
1942: PageFlowRequestProcessor getRequestProcessor() {
1943: ModuleConfig mc = getModuleConfig();
1944: String key = Globals.REQUEST_PROCESSOR_KEY + mc.getPrefix();
1945: RequestProcessor rp = (RequestProcessor) getServletContext()
1946: .getAttribute(key);
1947:
1948: //
1949: // The RequestProcessor may not have been initialized -- if so, just return a new (temporary) one.
1950: //
1951: if (rp == null) {
1952: try {
1953: ControllerConfig cc = mc.getControllerConfig();
1954: rp = (RequestProcessor) RequestUtils
1955: .applicationInstance(cc.getProcessorClass());
1956: rp.init(InternalUtils
1957: .getActionServlet(getServletContext()), mc);
1958: } catch (Exception e) {
1959: _log.error(
1960: "Could not initialize request processor for module "
1961: + mc.getPrefix(), e);
1962: }
1963: }
1964:
1965: assert rp == null || rp instanceof PageFlowRequestProcessor : rp
1966: .getClass().getName();
1967: return (PageFlowRequestProcessor) rp;
1968: }
1969:
1970: /**
1971: * Create a raw action URI, which can be modified before being sent through the registered URL rewriting chain
1972: * using {@link org.apache.beehive.netui.core.urls.URLRewriterService#rewriteURL}.
1973: *
1974: * @param actionName the action name to convert into a MutableURI; may be qualified with a path from the webapp
1975: * root, in which case the parent directory from the current request is <i>not</i> used.
1976: * @return a MutableURI for the given action, suitable for URL rewriting.
1977: * @throws URISyntaxException if there is a problem converting the action URI (derived
1978: * from processing the given action name) into a MutableURI.
1979: * @throws IllegalStateException if this method is invoked outside of action method
1980: * execution (i.e., outside of the call to {@link FlowController#execute},
1981: * and outside of {@link FlowController#onCreate},
1982: * {@link FlowController#beforeAction}, {@link FlowController#afterAction}.
1983: */
1984: public MutableURI getActionURI(String actionName)
1985: throws URISyntaxException {
1986: if (_perRequestState == null) {
1987: throw new IllegalStateException(
1988: "getActionURI was called outside of a valid context.");
1989: }
1990: ServletContext servletContext = getServletContext();
1991: HttpServletRequest request = getRequest();
1992: HttpServletResponse response = getResponse();
1993:
1994: return PageFlowUtils.getActionURI(servletContext, request,
1995: response, actionName);
1996: }
1997:
1998: /**
1999: * Create a fully-rewritten URI given an action and parameters.
2000: *
2001: * @param actionName the action name to convert into a fully-rewritten URI; may be qualified with a path from the
2002: * webapp root, in which case the parent directory from the current request is <i>not</i> used.
2003: * @param parameters the additional parameters to include in the URI query.
2004: * @param asValidXml flag indicating that the query of the uri should be written
2005: * using the "&amp;" entity, rather than the character, '&'
2006: * @return a fully-rewritten URI for the given action.
2007: * @throws URISyntaxException if there is a problem converting the action url (derived
2008: * from processing the given action name) into a URI.
2009: * @throws IllegalStateException if this method is invoked outside of action method
2010: * execution (i.e., outside of the call to {@link FlowController#execute},
2011: * and outside of {@link FlowController#onCreate},
2012: * {@link FlowController#beforeAction}, {@link FlowController#afterAction}.
2013: */
2014: public String getRewrittenActionURI(String actionName,
2015: Map parameters, boolean asValidXml)
2016: throws URISyntaxException {
2017: if (_perRequestState == null) {
2018: throw new IllegalStateException(
2019: "getRewrittenActionURI was called outside of a valid context.");
2020: }
2021: ServletContext servletContext = getServletContext();
2022: HttpServletRequest request = getRequest();
2023: HttpServletResponse response = getResponse();
2024:
2025: return PageFlowUtils.getRewrittenActionURI(servletContext,
2026: request, response, actionName, parameters, null,
2027: asValidXml);
2028: }
2029:
2030: FlowControllerHandlerContext getHandlerContext() {
2031: return new FlowControllerHandlerContext(getRequest(),
2032: getResponse(), this);
2033: }
2034: }
|