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.JavaControlUtils;
022: import org.apache.beehive.netui.pageflow.handler.Handlers;
023: import org.apache.beehive.netui.util.logging.Logger;
024:
025: import javax.servlet.http.HttpSessionBindingListener;
026: import javax.servlet.http.HttpServletRequest;
027: import javax.servlet.http.HttpServletResponse;
028: import javax.servlet.http.HttpSession;
029: import javax.servlet.http.HttpSessionBindingEvent;
030: import javax.servlet.ServletContext;
031: import java.io.Serializable;
032: import java.lang.reflect.Field;
033:
034: /**
035: * Base class for Page Flow managed objects (like page flows and JavaServer Faces backing beans).
036: */
037: public abstract class PageFlowManagedObject implements Serializable,
038: HttpSessionBindingListener {
039: private static final Logger _log = Logger
040: .getInstance(PageFlowManagedObject.class);
041:
042: /**
043: * Reference to the current ServletContext.
044: */
045: private transient ServletContext _servletContext;
046:
047: /**
048: * Creation time. This is non-transient, so it gets replicated in a cluster.
049: */
050: private long _createTime;
051:
052: /**
053: * Flag used to detect multiple {@link #valueUnbound(javax.servlet.http.HttpSessionBindingEvent)} events.
054: * Depending on the {@link org.apache.beehive.netui.pageflow.handler.StorageHandler} implementation used,
055: * it is possible for the framework and the Servlet container to dispatch multiple unbinding events signaling
056: * either the real or virtual removal of this object from the session. If the removal was virtual with a
057: * real removal to follow later, two (or more) {@link #valueUnbound(javax.servlet.http.HttpSessionBindingEvent)}
058: * events could be received by this object. This flag prevents this with the assumption that once detached
059: * from the {@link HttpSession}, it is never reattached.
060: */
061: private boolean _isDestroyed = false;
062:
063: protected PageFlowManagedObject() {
064: _createTime = System.currentTimeMillis();
065: }
066:
067: /**
068: * Reinitialize the object for a new request. Used by the framework; normally should not be called directly.
069: */
070: public void reinitialize(HttpServletRequest request,
071: HttpServletResponse response, ServletContext servletContext) {
072: _servletContext = servletContext;
073: }
074:
075: /**
076: * Internal method to reinitialize only if necessary. The test is whether the ServletContext
077: * reference has been lost.
078: */
079: void reinitializeIfNecessary(HttpServletRequest request,
080: HttpServletResponse response, ServletContext servletContext) {
081: if (_servletContext == null) {
082: reinitialize(request, response, servletContext);
083: }
084: }
085:
086: /**
087: * Initialize after object creation. This is a framework-invoked method; it should not normally be called directly.
088: */
089: public synchronized void create(HttpServletRequest request,
090: HttpServletResponse response, ServletContext servletContext)
091: throws Exception {
092: reinitialize(request, response, servletContext);
093: JavaControlUtils.initJavaControls(request, response,
094: servletContext, this );
095: onCreate();
096: }
097:
098: /**
099: * Internal destroy method that is invoked when this object is being removed from the session. This is a
100: * framework-invoked method; it should not normally be called directly.
101: */
102: void destroy(HttpSession session) {
103: onDestroy(session);
104: JavaControlUtils.uninitJavaControls(
105: session.getServletContext(), this );
106: }
107:
108: /**
109: * Create-time callback. Occurs after internal initialization (e.g., Control fields) is done.
110: * @throws Exception
111: */
112: protected void onCreate() throws Exception {
113: }
114:
115: /**
116: * Callback that occurs when this object is "destroyed", i.e., removed from the session.
117: * @param session
118: */
119: protected void onDestroy(HttpSession session) {
120: }
121:
122: /**
123: * Callback when this object is added to the user session.
124: */
125: public void valueBound(HttpSessionBindingEvent event) {
126: }
127:
128: /**
129: * Callback when this object is removed from the user session. Causes {@link #onDestroy} to be called. This is a
130: * framework-invoked method that should not be called directly.
131: */
132: public void valueUnbound(HttpSessionBindingEvent event) {
133: //
134: // We may have lost our transient ServletContext reference. Try to get the ServletContext reference from the
135: // HttpSession object if necessary.
136: //
137: ServletContext servletContext = getServletContext();
138: HttpSession session = event.getSession();
139:
140: if (servletContext == null && session != null) {
141: servletContext = session.getServletContext();
142: }
143:
144: if (servletContext != null) {
145: if (!_isDestroyed
146: && Handlers.get(servletContext).getStorageHandler()
147: .allowBindingEvent(event)) {
148: destroy(session);
149: _isDestroyed = true;
150: }
151: }
152: }
153:
154: /**
155: * Remove this instance from the session.
156: */
157: public abstract void removeFromSession(HttpServletRequest request);
158:
159: /**
160: * Store this object in the user session, in the appropriate place. Used by the framework; normally should not be
161: * called directly.
162: */
163: public abstract void persistInSession(HttpServletRequest request,
164: HttpServletResponse response);
165:
166: /**
167: * Ensures that any changes to this object will be replicated in a cluster (for failover),
168: * even if the replication scheme uses a change-detection algorithm that relies on
169: * HttpSession.setAttribute to be aware of changes. Note that this method is used by the framework
170: * and does not need to be called explicitly in most cases.
171: *
172: * @param request the current HttpServletRequest
173: */
174: public abstract void ensureFailover(HttpServletRequest request);
175:
176: /**
177: * Get the current ServletContext.
178: */
179: protected ServletContext getServletContext() {
180: return _servletContext;
181: }
182:
183: /**
184: * Get the display name for this managed object.
185: */
186: public abstract String getDisplayName();
187:
188: /**
189: * Tell whether the given Field is uninitialized.
190: * @return <code>true</code> if the field is non-<code>null</code> and its value is <code>null</code>.
191: */
192: protected boolean fieldIsUninitialized(Field field) {
193: try {
194: return field != null && field.get(this ) == null;
195: } catch (IllegalAccessException e) {
196: _log.error("Error initializing field " + field.getName()
197: + " in " + getDisplayName(), e);
198: return false;
199: }
200: }
201:
202: /**
203: * Initialize the given field with an instance. Mainly useful for the error handling.
204: */
205: protected void initializeField(Field field, Object instance) {
206: if (instance != null) {
207: if (_log.isTraceEnabled()) {
208: _log.trace("Initializing field " + field.getName()
209: + " in " + getDisplayName() + " with "
210: + instance);
211: }
212:
213: try {
214: field.set(this , instance);
215: } catch (IllegalArgumentException e) {
216: _log.error("Could not set field " + field.getName()
217: + " on " + getDisplayName()
218: + "; instance is of type "
219: + instance.getClass().getName()
220: + ", field type is "
221: + field.getType().getName());
222: } catch (IllegalAccessException e) {
223: _log.error("Error initializing field "
224: + field.getName() + " in " + getDisplayName(),
225: e);
226: }
227: }
228: }
229:
230: /**
231: * Get the time at which this object was created.
232: * @return the system time, in milliseconds, at which this object was created.
233: */
234: public long getCreateTime() {
235: return _createTime;
236: }
237:
238: /**
239: * Package protected getter that provides access to a read-only property about the current state
240: * of this Page Flow. If it has been destroyed in the middle of a request, this flag will return
241: * <code>true</code> and <code>false</code> otherwise.
242: *
243: * @return <code>true</code> if this object has been destroyed; <code>false</code> otherwise.
244: */
245: boolean isDestroyed() {
246: return _isDestroyed;
247: }
248: }
|