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.internal.InternalConstants;
022: import org.apache.beehive.netui.pageflow.internal.InternalUtils;
023: import org.apache.beehive.netui.pageflow.internal.CachedFacesBackingInfo;
024: import org.apache.beehive.netui.pageflow.internal.CachedSharedFlowRefInfo;
025: import org.apache.beehive.netui.pageflow.scoping.ScopedServletUtils;
026: import org.apache.beehive.netui.pageflow.handler.Handlers;
027: import org.apache.beehive.netui.pageflow.handler.StorageHandler;
028: import org.apache.beehive.netui.util.internal.cache.ClassLevelCache;
029: import org.apache.beehive.netui.util.logging.Logger;
030: import org.apache.beehive.controls.api.context.ControlContainerContext;
031:
032: import javax.servlet.http.HttpServletRequest;
033: import javax.servlet.http.HttpServletResponse;
034: import javax.servlet.ServletContext;
035: import java.util.Map;
036: import java.util.Collections;
037: import java.lang.reflect.Field;
038:
039: /**
040: * <p>
041: * A JavaServer Faces backing bean. An instance of this class will be created whenever a corresponding JSF path is
042: * requested (e.g., an instance of foo.MyPage will be created for the webapp-relative path "/foo/MyPage.faces"). The
043: * instance will be released (removed from the user session) when a non-matching path is requested. A faces backing
044: * bean can hold component references and event/command handlers, and it can raise actions with normal JSF command event
045: * handlers that are annotated with {@link org.apache.beehive.netui.pageflow.annotations.Jpf.CommandHandler @Jpf.CommandHandler}.
046: * The bean instance can be bound to with a JSF-style expression like <code>#{backing.myComponent}</code>.
047: * </p>
048: * <p>
049: * JSF backing beans are configured using the
050: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.FacesBacking @Jpf.FacesBacking} annotation.
051: * </p>
052: */
053: public abstract class FacesBackingBean extends PageFlowManagedObject {
054: private static final String CACHED_INFO_KEY = "cachedInfo";
055: private static final Logger _log = Logger
056: .getInstance(FacesBackingBean.class);
057:
058: private Map _pageInputs;
059:
060: /**
061: * The bean context associated with this request
062: */
063: ControlContainerContext _beanContext;
064:
065: /**
066: * Store this object in the user session, in the appropriate place. Used by the framework; normally should not be
067: * called directly.
068: */
069: public void persistInSession(HttpServletRequest request,
070: HttpServletResponse response) {
071: StorageHandler sh = Handlers.get(getServletContext())
072: .getStorageHandler();
073: HttpServletRequest unwrappedRequest = PageFlowUtils
074: .unwrapMultipart(request);
075: RequestContext rc = new RequestContext(unwrappedRequest, null);
076: String attrName = ScopedServletUtils.getScopedSessionAttrName(
077: InternalConstants.FACES_BACKING_ATTR, unwrappedRequest);
078:
079: sh.setAttribute(rc, attrName, this );
080: }
081:
082: /**
083: * Remove this instance from the session.
084: */
085: public void removeFromSession(HttpServletRequest request) {
086: StorageHandler sh = Handlers.get(getServletContext())
087: .getStorageHandler();
088: HttpServletRequest unwrappedRequest = PageFlowUtils
089: .unwrapMultipart(request);
090: RequestContext rc = new RequestContext(unwrappedRequest, null);
091: String attrName = ScopedServletUtils.getScopedSessionAttrName(
092: InternalConstants.FACES_BACKING_ATTR, unwrappedRequest);
093:
094: sh.removeAttribute(rc, attrName);
095: }
096:
097: /**
098: * Ensures that any changes to this object will be replicated in a cluster (for failover),
099: * even if the replication scheme uses a change-detection algorithm that relies on
100: * HttpSession.setAttribute to be aware of changes. Note that this method is used by the framework
101: * and does not need to be called explicitly in most cases.
102: *
103: * @param request the current HttpServletRequest
104: */
105: public void ensureFailover(HttpServletRequest request) {
106: StorageHandler sh = Handlers.get(getServletContext())
107: .getStorageHandler();
108: HttpServletRequest unwrappedRequest = PageFlowUtils
109: .unwrapMultipart(request);
110: RequestContext rc = new RequestContext(unwrappedRequest, null);
111: String attr = ScopedServletUtils.getScopedSessionAttrName(
112: InternalConstants.FACES_BACKING_ATTR, unwrappedRequest);
113: sh.ensureFailover(rc, attr, this );
114: }
115:
116: /**
117: * Get the display name for the bean. For FacesBackingBeans, this is simply the class name.
118: */
119: public String getDisplayName() {
120: return getClass().getName();
121: }
122:
123: /**
124: * Reinitialize the bean for a new request. Used by the framework; normally should not be called directly.
125: */
126: public void reinitialize(HttpServletRequest request,
127: HttpServletResponse response, ServletContext servletContext) {
128: super .reinitialize(request, response, servletContext);
129:
130: if (_pageInputs == null) {
131: Map map = InternalUtils.getActionOutputMap(request, false);
132: if (map != null)
133: _pageInputs = Collections.unmodifiableMap(map);
134: }
135:
136: //
137: // Initialize the page flow field.
138: //
139: Field pageFlowMemberField = getCachedInfo()
140: .getPageFlowMemberField();
141:
142: // TODO: should we add a compiler warning if this field isn't transient? All this reinitialization logic is
143: // for the transient case.
144: if (fieldIsUninitialized(pageFlowMemberField)) {
145: PageFlowController pfc = PageFlowUtils.getCurrentPageFlow(
146: request, servletContext);
147: initializeField(pageFlowMemberField, pfc);
148: }
149:
150: //
151: // Initialize the shared flow fields.
152: //
153: CachedSharedFlowRefInfo.SharedFlowFieldInfo[] sharedFlowMemberFields = getCachedInfo()
154: .getSharedFlowMemberFields();
155:
156: if (sharedFlowMemberFields != null) {
157: for (int i = 0; i < sharedFlowMemberFields.length; i++) {
158: CachedSharedFlowRefInfo.SharedFlowFieldInfo fi = sharedFlowMemberFields[i];
159: Field field = fi.field;
160:
161: if (fieldIsUninitialized(field)) {
162: Map/*< String, SharedFlowController >*/sharedFlows = PageFlowUtils
163: .getSharedFlows(request);
164: String name = fi.sharedFlowName;
165: SharedFlowController sf = name != null ? (SharedFlowController) sharedFlows
166: .get(name)
167: : PageFlowUtils.getGlobalApp(request);
168:
169: if (sf != null) {
170: initializeField(field, sf);
171: } else {
172: _log
173: .error("Could not find shared flow with name \""
174: + fi.sharedFlowName
175: + "\" to initialize field "
176: + field.getName()
177: + " in "
178: + getClass().getName());
179: }
180: }
181: }
182: }
183: }
184:
185: /**
186: * Get a page input that was passed from a Page Flow action as an "action output".
187: *
188: * @param pageInputName the name of the page input. This is the same as the name of the "action output".
189: * @return the value of the page input, or <code>null</code> if the given one does not exist.
190: */
191: protected Object getPageInput(String pageInputName) {
192: return _pageInputs != null ? _pageInputs.get(pageInputName)
193: : null;
194: }
195:
196: /**
197: * Get the map of all page inputs that was passed from a Page Flow action as "action outputs".
198: *
199: * @return a Map of page-input-name (String) to page-input-value (Object).
200: */
201: public Map getPageInputMap() {
202: return _pageInputs;
203: }
204:
205: private CachedFacesBackingInfo getCachedInfo() {
206: ClassLevelCache cache = ClassLevelCache.getCache(getClass());
207: CachedFacesBackingInfo info = (CachedFacesBackingInfo) cache
208: .getCacheObject(CACHED_INFO_KEY);
209:
210: if (info == null) {
211: info = new CachedFacesBackingInfo(getClass(),
212: getServletContext());
213: cache.setCacheObject(CACHED_INFO_KEY, info);
214: }
215:
216: return info;
217: }
218:
219: /**
220: * Callback that is invoked when this backing bean is restored as the page itself is restored through a
221: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward @Jpf.Forward} or
222: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.SimpleAction @Jpf.SimpleAction} with
223: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward#navigateTo() navigateTo}=
224: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#currentPage Jpf.NavigateTo.currentPage}
225: * or
226: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Forward#navigateTo() navigateTo}=
227: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.NavigateTo#currentPage Jpf.NavigateTo.previousPage}.
228: */
229: protected void onRestore() {
230: }
231:
232: /**
233: * Restore this bean (set it as the current one from some dormant state). This is a framework-invoked method that
234: * should not normally be called directly.
235: *
236: * @param request the current HttpServletRequest.
237: * @param response the current HttpServletResponse.
238: * @param servletContext the current ServletContext.
239: */
240: public void restore(HttpServletRequest request,
241: HttpServletResponse response, ServletContext servletContext) {
242: reinitialize(request, response, servletContext);
243: StorageHandler sh = Handlers.get(getServletContext())
244: .getStorageHandler();
245: HttpServletRequest unwrappedRequest = PageFlowUtils
246: .unwrapMultipart(request);
247: RequestContext rc = new RequestContext(unwrappedRequest,
248: response);
249: String attrName = ScopedServletUtils.getScopedSessionAttrName(
250: InternalConstants.FACES_BACKING_ATTR, unwrappedRequest);
251: sh.setAttribute(rc, attrName, this );
252: Map newActionOutputs = InternalUtils.getActionOutputMap(
253: request, false);
254: if (newActionOutputs != null)
255: _pageInputs = newActionOutputs;
256: onRestore();
257: }
258: }
|