001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.pageflow;
020:
021: import org.apache.beehive.netui.pageflow.handler.Handlers;
022: import org.apache.beehive.netui.pageflow.handler.StorageHandler;
023: import org.apache.beehive.netui.pageflow.internal.InternalConstants;
024: import org.apache.beehive.netui.pageflow.scoping.ScopedServletUtils;
025: import org.apache.beehive.netui.util.internal.cache.ClassLevelCache;
026: import org.apache.struts.action.ActionForm;
027: import org.apache.struts.action.ActionForward;
028: import org.apache.struts.action.ActionMapping;
029:
030: import javax.servlet.ServletContext;
031: import javax.servlet.http.HttpServletRequest;
032: import javax.servlet.http.HttpServletResponse;
033:
034: /**
035: * <p>
036: * Base "shared flow" class for controller logic, exception handlers, and state that can be shared by any number of page
037: * flows. A shared flow is <i>not</i> a page flow; it is used by page flows, but never becomes the "current page flow"
038: * (see {@link PageFlowController} for information on page flows and the "current page flow").
039: * The class is configured through the
040: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller @Jpf.Controller} annotation.
041: * </p>
042: *
043: * <p>
044: * A shared flow comes into existance in one of two ways:
045: * <ul>
046: * <li>
047: * A page flow is hit, and the page flow refers to the shared flow in its
048: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller#sharedFlowRefs sharedFlowRefs}
049: * annotation attribute, or
050: * </li>
051: * <li>
052: * Any page flow is hit, and the <code><default-shared-flow-refs></code> element in
053: * /WEB-INF/beehive-netui-config.xml declares that this shared flow will be used by all page flows in the web
054: * application.
055: * </li>
056: * </ul>
057: * When a shared flow is created, it is stored in the user session. It is only removed through a call to
058: * {@link #remove} or through a call to {@link PageFlowUtils#removeSharedFlow}.
059: * </p>
060: *
061: * <p>
062: * Shared flow actions are defined with <i>action methods</i> or <i>action annotations</i> that determine the next URI
063: * to be displayed, after optionally performing arbitrary logic. A page or page flow can raise a shared flow action
064: * using the pattern <code>"</code><i>shared-flow-name</i><code>.</code><i>action-name</i><code>"</code>. The shared
065: * flow name is the one chosen by the page flow
066: * in {@link org.apache.beehive.netui.pageflow.annotations.Jpf.SharedFlowRef#name name}
067: * on {@link org.apache.beehive.netui.pageflow.annotations.Jpf.SharedFlowRef @Jpf.SharedFlowRef}.
068: * </p>
069: *
070: * <p>
071: * A referenced shared flow gets the chance to handle any uncaught page flow exception. It declares its exception
072: * handling through {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller#catches catches}
073: * on {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller @Jpf.Controller}.
074: * </p>
075: *
076: * <p>
077: * Properties in the current shared flow instance can be accessed from JSP 2.0-style expressions like this one:
078: * <code>${sharedFlow.</code><i>sharedFlowName</i><code>.someProperty}</code>.
079: * </p>
080: *
081: * <p>
082: * There may only be one shared flow in any package.
083: * </p>
084: *
085: * @see PageFlowController
086: */
087: public abstract class SharedFlowController extends FlowController
088: implements PageFlowConstants {
089: private static final String CACHED_INFO_KEY = "cachedInfo";
090:
091: /**
092: * Get the Struts module path for actions in this shared flow.
093: *
094: * @return the Struts module path for actions in this shared flow.
095: */
096: public String getModulePath() {
097: ClassLevelCache cache = ClassLevelCache.getCache(getClass());
098: String modulePath = (String) cache
099: .getCacheObject(CACHED_INFO_KEY);
100:
101: if (modulePath == null) {
102: modulePath = "/-";
103: String className = getClass().getName();
104: int lastDot = className.lastIndexOf('.');
105: if (lastDot != -1) {
106: className = className.substring(0, lastDot);
107: modulePath += className.replace('.', '/');
108: }
109: cache.setCacheObject(CACHED_INFO_KEY, modulePath);
110: }
111:
112: return modulePath;
113: }
114:
115: /**
116: * Store this object in the user session, in the appropriate place. Used by the framework; normally should not be
117: * called directly.
118: */
119: public void persistInSession(HttpServletRequest request,
120: HttpServletResponse response) {
121: StorageHandler sh = Handlers.get(getServletContext())
122: .getStorageHandler();
123: HttpServletRequest unwrappedRequest = PageFlowUtils
124: .unwrapMultipart(request);
125: RequestContext rc = new RequestContext(unwrappedRequest, null);
126: String attrName = ScopedServletUtils.getScopedSessionAttrName(
127: InternalConstants.SHARED_FLOW_ATTR_PREFIX
128: + getClass().getName(), unwrappedRequest);
129: sh.setAttribute(rc, attrName, this );
130: }
131:
132: /**
133: * Ensures that any changes to this object will be replicated in a cluster (for failover),
134: * even if the replication scheme uses a change-detection algorithm that relies on
135: * HttpSession.setAttribute to be aware of changes. Note that this method is used by the framework
136: * and does not need to be called explicitly in most cases.
137: *
138: * @param request the current HttpServletRequest
139: */
140: public void ensureFailover(HttpServletRequest request) {
141: StorageHandler sh = Handlers.get(getServletContext())
142: .getStorageHandler();
143: HttpServletRequest unwrappedRequest = PageFlowUtils
144: .unwrapMultipart(request);
145: RequestContext rc = new RequestContext(unwrappedRequest, null);
146: String attrName = ScopedServletUtils.getScopedSessionAttrName(
147: InternalConstants.SHARED_FLOW_ATTR_PREFIX
148: + getClass().getName(), request);
149: sh.ensureFailover(rc, attrName, this );
150: }
151:
152: /**
153: * Get the URI.
154: * @return <code>null</code>, as this object is not URL-addressible.
155: */
156: public String getURI() {
157: return "/";
158: }
159:
160: /**
161: * Get the display name. The display name for a shared flow is simply the class name.
162: * @return the name of the shared flow class.
163: */
164: public String getDisplayName() {
165: return getClass().getName();
166: }
167:
168: /**
169: * Get a legacy PreviousPageInfo.
170: * @deprecated This method will be removed without replacement in a future release.
171: */
172: public PreviousPageInfo getPreviousPageInfoLegacy(
173: PageFlowController curJpf, HttpServletRequest request) {
174: assert curJpf != null;
175: return curJpf.getCurrentPageInfo();
176: }
177:
178: /**
179: * Store information about recent pages displayed. This is a framework-invoked method that should not normally be
180: * called directly.
181: */
182: public void savePreviousPageInfo(ActionForward forward,
183: ActionForm form, ActionMapping mapping,
184: HttpServletRequest request, ServletContext servletContext,
185: boolean isSpecialForward) {
186: //
187: // Special case: if the given forward has a path to a page in the current pageflow, let that pageflow save
188: // the info on this page. Otherwise, don't ever save any info on what we're forwarding to.
189: //
190: if (!isSpecialForward && forward != null) // i.e., it's a straight Forward to a path, not a navigateTo, etc.
191: {
192: PageFlowController currentJpf = PageFlowUtils
193: .getCurrentPageFlow(request, getServletContext());
194:
195: if (currentJpf != null) {
196: if (forward.getContextRelative()
197: && forward.getPath().startsWith(
198: currentJpf.getModulePath())) {
199: currentJpf.savePreviousPageInfo(forward, form,
200: mapping, request, servletContext,
201: isSpecialForward);
202: }
203: }
204: }
205: }
206:
207: /**
208: * Store information about the most recent action invocation. This is a framework-invoked method that should not
209: * normally be called directly
210: */
211: void savePreviousActionInfo(ActionForm form,
212: HttpServletRequest request, ActionMapping mapping,
213: ServletContext servletContext) {
214: //
215: // Save this previous-action info in the *current page flow*.
216: //
217: PageFlowController currentJpf = PageFlowUtils
218: .getCurrentPageFlow(request, getServletContext());
219: if (currentJpf != null)
220: currentJpf.savePreviousActionInfo(form, request, mapping,
221: servletContext);
222: }
223:
224: /**
225: * Remove this instance from the session. When inside a shared flow action, {@link #remove} may be called instead.
226: */
227: public synchronized void removeFromSession(
228: HttpServletRequest request) {
229: PageFlowUtils.removeSharedFlow(getClass().getName(), request);
230: }
231: }
|