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.core.factory.Factory;
022: import org.apache.beehive.netui.core.factory.FactoryUtils;
023: import org.apache.beehive.netui.pageflow.internal.InternalConstants;
024: import org.apache.beehive.netui.pageflow.internal.InternalUtils;
025: import org.apache.beehive.netui.pageflow.internal.AnnotationReader;
026: import org.apache.beehive.netui.pageflow.handler.Handlers;
027: import org.apache.beehive.netui.pageflow.handler.ReloadableClassHandler;
028: import org.apache.beehive.netui.util.internal.FileUtils;
029: import org.apache.beehive.netui.util.logging.Logger;
030: import org.apache.beehive.netui.util.config.ConfigUtil;
031: import org.apache.beehive.netui.util.config.bean.PageFlowFactoriesConfig;
032: import org.apache.beehive.netui.util.config.bean.PageFlowFactoryConfig;
033:
034: import javax.servlet.http.HttpServletRequest;
035: import javax.servlet.http.HttpServletResponse;
036: import javax.servlet.ServletContext;
037:
038: /**
039: * Factory for creating "backing beans" for JavaServer Faces pages.
040: */
041: public class FacesBackingBeanFactory extends Factory implements
042: InternalConstants {
043: private static final Logger _log = Logger
044: .getInstance(FacesBackingBeanFactory.class);
045:
046: private static final String CONTEXT_ATTR = InternalConstants.ATTR_PREFIX
047: + "jsfBackingFactory";
048:
049: private ReloadableClassHandler _rch;
050:
051: protected void onCreate() {
052: }
053:
054: protected FacesBackingBeanFactory() {
055: }
056:
057: /**
058: * Initialize an instance of this class in the ServletContext. This is a framework-invoked method and should
059: * normally be called directly.
060: */
061: public static void init(ServletContext servletContext) {
062: PageFlowFactoriesConfig factoriesBean = ConfigUtil.getConfig()
063: .getPageFlowFactories();
064: FacesBackingBeanFactory factory = null;
065:
066: if (factoriesBean != null) {
067: PageFlowFactoryConfig fcFactoryBean = factoriesBean
068: .getFacesBackingBeanFactory();
069: factory = (FacesBackingBeanFactory) FactoryUtils
070: .getFactory(servletContext, fcFactoryBean,
071: FacesBackingBeanFactory.class);
072: }
073:
074: if (factory == null)
075: factory = new FacesBackingBeanFactory();
076: factory.reinit(servletContext);
077:
078: servletContext.setAttribute(CONTEXT_ATTR, factory);
079: }
080:
081: /**
082: * Called to reinitialize this instance, most importantly after it has been serialized/deserialized.
083: *
084: * @param servletContext the current ServletContext.
085: */
086: protected void reinit(ServletContext servletContext) {
087: super .reinit(servletContext);
088: if (_rch == null)
089: _rch = Handlers.get(servletContext)
090: .getReloadableClassHandler();
091: }
092:
093: /**
094: * Get a FacesBackingBeanFactory.
095: *
096: * @param servletContext the current ServletContext.
097: * @return a FacesBackingBeanFactory for the given ServletContext. It may or may not be a cached instance.
098: */
099: public static FacesBackingBeanFactory get(
100: ServletContext servletContext) {
101: FacesBackingBeanFactory factory = (FacesBackingBeanFactory) servletContext
102: .getAttribute(CONTEXT_ATTR);
103: assert factory != null : FacesBackingBeanFactory.class
104: .getName()
105: + " was not found in ServletContext attribute "
106: + CONTEXT_ATTR;
107: factory.reinit(servletContext);
108: return factory;
109: }
110:
111: /**
112: * Get the "backing bean" associated with the JavaServer Faces page for a request.
113: *
114: * @param requestContext a {@link RequestContext} object which contains the current request and response.
115: */
116: public FacesBackingBean getFacesBackingBeanForRequest(
117: RequestContext requestContext) {
118: HttpServletRequest request = requestContext.getHttpRequest();
119: String uri = InternalUtils.getDecodedServletPath(request);
120: assert uri.charAt(0) == '/' : uri;
121: String backingClassName = FileUtils.stripFileExtension(uri
122: .substring(1).replace('/', '.'));
123: FacesBackingBean currentBean = InternalUtils
124: .getFacesBackingBean(request, getServletContext());
125:
126: //
127: // If there is no current backing bean, or if the current one doesn't match the desired classname, create one.
128: //
129: if (currentBean == null
130: || !currentBean.getClass().getName().equals(
131: backingClassName)) {
132: FacesBackingBean bean = null;
133:
134: if (FileUtils.uriEndsWith(uri, FACES_EXTENSION)
135: || FileUtils.uriEndsWith(uri, JSF_EXTENSION)) {
136: bean = loadFacesBackingBean(requestContext,
137: backingClassName);
138:
139: //
140: // If we didn't create (or failed to create) a backing bean, and if this is a JSF request, then create
141: // a default one. This ensures that there will be a place for things like page inputs, that get stored
142: // in the backing bean across postbacks to the same JSF.
143: //
144: if (bean == null)
145: bean = new DefaultFacesBackingBean();
146:
147: //
148: // If we created a backing bean, invoke its create callback, and tell it to store itself in the session.
149: //
150: if (bean != null) {
151: HttpServletResponse response = requestContext
152: .getHttpResponse();
153:
154: try {
155: bean.create(request, response,
156: getServletContext());
157: } catch (Exception e) {
158: _log.error(
159: "Error while creating backing bean instance of "
160: + backingClassName, e);
161: }
162:
163: bean.persistInSession(request, response);
164: return bean;
165: }
166: }
167:
168: //
169: // We didn't create a backing bean. If there's one in the session (an inappropriate one), remove it.
170: //
171: InternalUtils.removeCurrentFacesBackingBean(request,
172: getServletContext());
173: } else if (currentBean != null) {
174: if (_log.isDebugEnabled()) {
175: _log.debug("Using existing backing bean instance "
176: + currentBean + " for request "
177: + request.getRequestURI());
178: }
179:
180: currentBean.reinitialize(request, requestContext
181: .getHttpResponse(), getServletContext());
182: }
183:
184: return currentBean;
185: }
186:
187: /**
188: * Load a "backing bean" associated with the JavaServer Faces page for a request.
189: * @param requestContext a {@link RequestContext} object which contains the current request and response.
190: * @param backingClassName the name of the backing bean class.
191: * @return an initialized FacesBackingBean, or <code>null</code> if an error occurred.
192: */
193: protected FacesBackingBean loadFacesBackingBean(
194: RequestContext requestContext, String backingClassName) {
195: try {
196: Class backingClass = null;
197:
198: try {
199: backingClass = getFacesBackingBeanClass(backingClassName);
200: } catch (ClassNotFoundException e) {
201: // ignore -- we deal with this and log this immediately below. getFacesBackingBeanClass() by default
202: // does not throw this exception, but a derived version might.
203: }
204:
205: if (backingClass == null) {
206: if (_log.isTraceEnabled()) {
207: _log.trace("No backing bean class "
208: + backingClassName
209: + " found for request "
210: + requestContext.getHttpRequest()
211: .getRequestURI());
212: }
213: } else {
214: AnnotationReader annReader = AnnotationReader
215: .getAnnotationReader(backingClass,
216: getServletContext());
217:
218: if (annReader.getJpfAnnotation(backingClass,
219: "FacesBacking") != null) {
220: if (_log.isDebugEnabled()) {
221: _log.debug("Found backing class "
222: + backingClassName
223: + " for request "
224: + requestContext.getHttpRequest()
225: .getRequestURI()
226: + "; creating a new instance.");
227: }
228:
229: return getFacesBackingBeanInstance(backingClass);
230: } else {
231: if (_log.isDebugEnabled()) {
232: _log.debug("Found matching backing class "
233: + backingClassName
234: + " for request "
235: + requestContext.getHttpRequest()
236: .getRequestURI()
237: + ", but it does not have the "
238: + ANNOTATION_QUALIFIER + "FacesBacking"
239: + " annotation.");
240: }
241: }
242: }
243: } catch (InstantiationException e) {
244: _log.error("Could not create backing bean instance of "
245: + backingClassName, e);
246: } catch (IllegalAccessException e) {
247: _log.error("Could not create backing bean instance of "
248: + backingClassName, e);
249: }
250:
251: return null;
252: }
253:
254: private static class DefaultFacesBackingBean extends
255: FacesBackingBean {
256: }
257:
258: /**
259: * Get a FacesBackingBean class. By default, this loads the class using the thread context class loader.
260: *
261: * @param className the name of the {@link FacesBackingBean} class to load.
262: * @return the loaded {@link FacesBackingBean} class.
263: * @throws ClassNotFoundException if the requested class could not be found.
264: */
265: public Class getFacesBackingBeanClass(String className)
266: throws ClassNotFoundException {
267: return _rch.loadCachedClass(className);
268: }
269:
270: /**
271: * Get a FacesBackingBean instance, given a FacesBackingBean class.
272: *
273: * @param beanClass the Class, which must be assignable to {@link FacesBackingBean}.
274: * @return a new FacesBackingBean instance.
275: */
276: public FacesBackingBean getFacesBackingBeanInstance(Class beanClass)
277: throws InstantiationException, IllegalAccessException {
278: assert FacesBackingBean.class.isAssignableFrom(beanClass) : "Class "
279: + beanClass.getName()
280: + " does not extend "
281: + FacesBackingBean.class.getName();
282: return (FacesBackingBean) beanClass.newInstance();
283: }
284: }
|