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 java.util.Map;
022: import java.util.LinkedHashMap;
023: import java.util.Iterator;
024: import java.lang.reflect.Modifier;
025: import javax.servlet.http.HttpServletRequest;
026: import javax.servlet.http.HttpServletResponse;
027: import javax.servlet.ServletContext;
028:
029: import org.apache.beehive.netui.core.factory.Factory;
030: import org.apache.beehive.netui.core.factory.FactoryUtils;
031: import org.apache.beehive.netui.util.logging.Logger;
032: import org.apache.beehive.netui.util.config.ConfigUtil;
033: import org.apache.beehive.netui.util.config.bean.PageFlowFactoryConfig;
034: import org.apache.beehive.netui.util.config.bean.PageFlowFactoriesConfig;
035: import org.apache.beehive.netui.util.config.bean.PageFlowConfig;
036: import org.apache.beehive.netui.util.config.bean.SharedFlowRefConfig;
037: import org.apache.beehive.netui.pageflow.internal.InternalUtils;
038: import org.apache.beehive.netui.pageflow.internal.InternalConstants;
039: import org.apache.beehive.netui.pageflow.internal.PageFlowRequestWrapper;
040: import org.apache.beehive.netui.pageflow.config.PageFlowControllerConfig;
041: import org.apache.beehive.netui.pageflow.handler.ReloadableClassHandler;
042: import org.apache.beehive.netui.pageflow.handler.Handlers;
043: import org.apache.struts.config.ModuleConfig;
044: import org.apache.struts.config.ControllerConfig;
045:
046: /**
047: * <p>
048: * Factory for creating {@link FlowController}s - user {@link PageFlowController}s and {@link SharedFlowController}s.
049: * </p>
050: */
051: public class FlowControllerFactory extends Factory {
052: private static final Logger LOG = Logger
053: .getInstance(FlowControllerFactory.class);
054:
055: private static final String CONTEXT_ATTR = InternalConstants.ATTR_PREFIX
056: + "fcFactory";
057: private static final String NO_GLOBAL_APP_KEY = InternalConstants.ATTR_PREFIX
058: + "noglobalapp";
059:
060: private transient ReloadableClassHandler _rch;
061:
062: protected FlowControllerFactory() {
063: }
064:
065: /**
066: * Initialize an instance of this class in the ServletContext. This is a framework-invoked
067: * method and should not normally be called directly.
068: */
069: public static void init(ServletContext servletContext) {
070: PageFlowFactoriesConfig factoriesBean = ConfigUtil.getConfig()
071: .getPageFlowFactories();
072: FlowControllerFactory factory = null;
073:
074: if (factoriesBean != null) {
075: PageFlowFactoryConfig fcFactoryBean = factoriesBean
076: .getPageFlowFactory();
077: factory = (FlowControllerFactory) FactoryUtils.getFactory(
078: servletContext, fcFactoryBean,
079: FlowControllerFactory.class);
080: }
081:
082: if (factory == null)
083: factory = new FlowControllerFactory();
084:
085: factory.reinit(servletContext);
086:
087: servletContext.setAttribute(CONTEXT_ATTR, factory);
088: }
089:
090: /**
091: * Called to reinitialize this instance, most importantly after it has been serialized/deserialized.
092: *
093: * @param servletContext the current ServletContext.
094: */
095: protected void reinit(ServletContext servletContext) {
096: super .reinit(servletContext);
097: if (_rch == null)
098: _rch = Handlers.get(servletContext)
099: .getReloadableClassHandler();
100: }
101:
102: /**
103: * Get a {@link FlowControllerFactory}. The instance returned may or may not have been cached.
104: *
105: * @param servletContext the current {@link ServletContext}.
106: * @return a {@link FlowControllerFactory} for the given {@link ServletContext}.
107: */
108: public static FlowControllerFactory get(
109: ServletContext servletContext) {
110: FlowControllerFactory factory = (FlowControllerFactory) servletContext
111: .getAttribute(CONTEXT_ATTR);
112: assert factory != null : FlowControllerFactory.class.getName()
113: + " was not found in ServletContext attribute "
114: + CONTEXT_ATTR;
115: factory.reinit(servletContext);
116: return factory;
117: }
118:
119: /**
120: * Get the page flow instance that should be associated with the given request. If it doesn't exist, create it.
121: * If one is created, the page flow stack (for nesting) will be cleared or pushed, and the new instance will be
122: * stored as the current page flow.
123: *
124: * @param context a {@link RequestContext} object which contains the current request and response.
125: * @return the {@link PageFlowController} for the request, or <code>null</code> if none was found.
126: */
127: public PageFlowController getPageFlowForRequest(
128: RequestContext context) throws InstantiationException,
129: IllegalAccessException {
130: String servletPath = InternalUtils
131: .getDecodedServletPath(context.getHttpRequest());
132: return getPageFlowForPath(context, servletPath);
133: }
134:
135: /**
136: * Get the page flow instance that should be associated with the given path. If it doesn't exist, create it.
137: * If one is created, the page flow stack (for nesting) will be cleared or pushed, and the new instance will be
138: * stored as the current page flow.
139: *
140: * @param context a {@link RequestContext} object which contains the current request and response.
141: * @param path a <strong>webapp-relative</strong> path. The path should not contain the webapp context path.
142: * @return the {@link PageFlowController} for the given path, or <code>null</code> if none was found.
143: */
144: public PageFlowController getPageFlowForPath(
145: RequestContext context, String path)
146: throws InstantiationException, IllegalAccessException {
147: HttpServletRequest request = context.getHttpRequest();
148: HttpServletResponse response = context.getHttpResponse();
149: PageFlowController cur = PageFlowUtils.getCurrentPageFlow(
150: request, getServletContext());
151: String parentDir = PageFlowUtils
152: .getModulePathForRelativeURI(path);
153:
154: //
155: // Reinitialize transient data that may have been lost on session failover.
156: //
157: if (cur != null)
158: cur.reinitialize(request, response, getServletContext());
159:
160: //
161: // If there's no current PageFlow, or if the current PageFlowController has a module path that
162: // is incompatible with the current request URI, then create the appropriate PageFlowController.
163: //
164: if (cur == null
165: || !PageFlowUtils.getModulePathForRelativeURI(
166: cur.getURI()).equals(parentDir)) {
167: String className = null;
168: try {
169: className = InternalUtils.getFlowControllerClassName(
170: parentDir, request, getServletContext());
171: return className != null ? createPageFlow(context,
172: className) : null;
173: } catch (ClassNotFoundException e) {
174: if (LOG.isInfoEnabled())
175: LOG.info("No page flow exists for path " + path
176: + ". Unable to load class \"" + className
177: + "\". Cause: " + e, e);
178: return null;
179: }
180: }
181:
182: return cur;
183: }
184:
185: /**
186: * Create a {@link PageFlowController} of the given type. The {@link PageFlowController} stack used
187: * for nesting will be cleared or pushed, and the new instance will be stored as the current
188: * {@link PageFlowController}.
189: *
190: * @param context a {@link RequestContext} object which contains the current request and response.
191: * @param pageFlowClassName the type name of the desired page flow.
192: * @return the newly-created PageFlowController, or <code>null</code> if none was found.
193: */
194: public PageFlowController createPageFlow(RequestContext context,
195: String pageFlowClassName) throws ClassNotFoundException,
196: InstantiationException, IllegalAccessException {
197: Class pageFlowClass = getFlowControllerClass(pageFlowClassName);
198: return createPageFlow(context, pageFlowClass);
199: }
200:
201: /**
202: * Create a {@link PageFlowController} of the given type. The PageFlowController stack (for
203: * nesting) will be cleared or pushed, and the new instance will be stored as the current
204: * PageFlowController.
205: *
206: * @param context a {@link RequestContext} object which contains the current request and response.
207: * @param pageFlowClass the type of the desired {@link PageFlowController}.
208: * @return the newly-created {@link PageFlowController}, or <code>null</code> if none was found.
209: */
210: public PageFlowController createPageFlow(RequestContext context,
211: Class pageFlowClass) throws InstantiationException,
212: IllegalAccessException {
213: assert PageFlowController.class.isAssignableFrom(pageFlowClass) : pageFlowClass
214: .getName();
215:
216: // If this is an abstract controller class, don't use it.
217: if (Modifier.isAbstract(pageFlowClass.getModifiers())) {
218: if (LOG.isInfoEnabled())
219: LOG.info("Unable to create Page Flow for class \""
220: + pageFlowClass.getName()
221: + "\" because the class is abstract");
222: return null;
223: }
224:
225: //
226: // First check if this is a request for a "long lived" page flow. If so, try
227: // PageFlowUtils.getCurrentPageFlow again, with the longLived flag.
228: //
229: HttpServletRequest request = context.getHttpRequest();
230: HttpServletResponse response = context.getHttpResponse();
231: ServletContext servletContext = getServletContext();
232: PageFlowController retVal = null;
233: // Make sure our request wrapper is in place.
234: request = PageFlowRequestWrapper.wrapRequest(request);
235: String modulePath = InternalUtils
236: .inferModulePathFromClassName(pageFlowClass.getName());
237: ModuleConfig mc = InternalUtils.ensureModuleConfig(modulePath,
238: servletContext);
239:
240: if (mc == null) {
241: LOG.error("Struts module " + modulePath + " not found for "
242: + pageFlowClass.getName()
243: + "; cannot create page flow.");
244: return null;
245: }
246:
247: if (InternalUtils.isLongLived(mc)) {
248: retVal = PageFlowUtils.getLongLivedPageFlow(modulePath,
249: request, servletContext);
250:
251: if (LOG.isDebugEnabled()) {
252: if (retVal != null) {
253: LOG
254: .debug("Using long lived PageFlowController of type "
255: + pageFlowClass.getName());
256: }
257: }
258: }
259:
260: //
261: // First, see if this is a nested page flow that's already on the stack. Unless "renesting" is explicitly
262: // enabled, we don't want to allow another instance of this page flow to be nested. This is a common
263: // browser back-button problem:
264: // 1) request nested page flow A
265: // 2) request nested page flow B
266: // 3) press back button, and execute an action on A.
267: //
268: // This logic does not deal with immediate self-nesting (A->A), which is taken care of in
269: // PageFlowController.forwardTo(). Nested page flows can only self-nest by forwarding to the .jpf URI, not
270: // indirectly by executing actions on themselves (think about it -- that would be a disaster).
271: //
272: boolean createdNew = false;
273: boolean isNestable = InternalUtils.isNestable(mc);
274: PageFlowStack pfStack = PageFlowStack.get(request,
275: getServletContext(), false);
276:
277: if (isNestable && pfStack != null) {
278: PageFlowConfig options = ConfigUtil.getConfig()
279: .getPageFlowConfig();
280:
281: if (options == null || !options.isEnableSelfNesting()) {
282: PageFlowController existing = pfStack.popUntil(request,
283: pageFlowClass, true);
284:
285: if (existing != null) {
286: existing.persistInSession(request, response);
287: return existing;
288: } else {
289: //
290: // If the feature is enabled, crawl back through the nesting history.
291: // This is implemented below.
292: //
293: }
294: }
295: }
296:
297: //
298: // OK, if it's not an existing long lived page flow, and if this wasn't a nested page flow already on the
299: // stack, then create a new instance.
300: //
301: if (retVal == null) {
302: if (LOG.isDebugEnabled())
303: LOG.debug("Creating PageFlowController of type "
304: + pageFlowClass.getName());
305:
306: retVal = (PageFlowController) getFlowControllerInstance(pageFlowClass);
307: createdNew = true;
308: }
309:
310: //
311: // Store the previous PageFlowController on the nesting stack (if this one is nestable),
312: // or destroy the nesting stack.
313: //
314: if (isNestable) {
315: //
316: // Call create() on the newly-created page flow. Note, the PageFlowController is responsible for
317: // driving the appropriate Control lifecycle.
318: //
319: if (createdNew)
320: retVal.create(request, response, servletContext);
321:
322: PageFlowController current = PageFlowUtils
323: .getCurrentPageFlow(request, getServletContext());
324:
325: if (current != null) {
326: if (LOG.isDebugEnabled())
327: LOG.debug("Pushing PageFlowController " + current
328: + " onto the nesting stack");
329:
330: if (pfStack == null)
331: pfStack = PageFlowStack.get(request,
332: getServletContext(), true);
333: pfStack.push(current, request);
334: }
335:
336: retVal.reinitialize(request, response, servletContext);
337: retVal.persistInSession(request, response);
338: }
339: //
340: // New page flow is not nestable; behave accordingly
341: //
342: else {
343: //
344: // A Page Flow nesting stack exists and needs to be destroyed.
345: //
346: if (pfStack != null) {
347: if (LOG.isDebugEnabled())
348: LOG
349: .debug("Destroying the PageFlowController stack.");
350:
351: //
352: // Start popping page flows until:
353: // 1) there are none left on the stack
354: // 2) we find one of the type we're returning.
355: // If (2), we'll use that one (this means that executing an action on a nesting page flow
356: // while in a nested one will not destroy the nesting page flow only to create a new instance of it).
357: //
358: PageFlowController onStackAlready = pfStack.popUntil(
359: request, retVal.getClass(), false);
360:
361: if (onStackAlready != null) {
362: if (LOG.isDebugEnabled())
363: LOG
364: .debug("Found a page flow of type "
365: + retVal.getClass()
366: + " in the stack; "
367: + "using that instance and stopping destruction of the nesting stack.");
368:
369: retVal = onStackAlready;
370: retVal.persistInSession(request, response);
371: } else {
372: //
373: // We're actually using the newly-created page flow, so call create() on it.
374: // Note that we make the call to persistInSession *before* create, so the previous flow's
375: // onDestroy() gets called before the new one's onCreate().
376: //
377: retVal.reinitialize(request, response,
378: servletContext);
379: retVal.persistInSession(request, response);
380: retVal.create(request, response, servletContext);
381: }
382: }
383: //
384: // There is no nesting stack, so just persist the new page flow
385: //
386: else {
387: //
388: // We're actually using the newly-created page flow, so take several steps that:
389: // 1. reinitialize the new page flow
390: // 2. persiste the new page flow in the session. Note, this causes the "destroy" lifecycle
391: // method to be invoked on the previous page flow which is still in the session.
392: // 3. if the page flow is newly created, invoke its "create" lifecycle method.
393: //
394: // Note, steps 2 and 3 must happen in this order in order to remove the previous page flow from
395: // the session before adding the new one.
396: //
397: // For example, this code will execute when forwarding from an existing Page Flow to a new Page Flow
398: //
399: retVal.reinitialize(request, response, servletContext);
400: retVal.persistInSession(request, response);
401: if (createdNew)
402: retVal.create(request, response, servletContext);
403: }
404: }
405:
406: return retVal;
407: }
408:
409: /**
410: * Create a {@link SharedFlowController} of the given type.
411: *
412: * @param context a {@link RequestContext} object which contains the current request and response.
413: * @param sharedFlowClassName the type name of the desired SharedFlowController.
414: * @return the newly-created SharedFlowController, or <code>null</code> if none was found.
415: */
416: public SharedFlowController createSharedFlow(
417: RequestContext context, String sharedFlowClassName)
418: throws ClassNotFoundException, InstantiationException,
419: IllegalAccessException {
420: Class sharedFlowClass = getFlowControllerClass(sharedFlowClassName);
421: return createSharedFlow(context, sharedFlowClass);
422: }
423:
424: /**
425: * Create a {@link SharedFlowController} of the given type.
426: *
427: * @param context a {@link RequestContext} object which contains the current request and response.
428: * @param sharedFlowClass the type of the desired SharedFlowController.
429: * @return the newly-created SharedFlowController, or <code>null</code> if none was found.
430: */
431: public SharedFlowController createSharedFlow(
432: RequestContext context, Class sharedFlowClass)
433: throws InstantiationException, IllegalAccessException {
434: assert SharedFlowController.class
435: .isAssignableFrom(sharedFlowClass) : sharedFlowClass
436: .getName();
437:
438: if (LOG.isDebugEnabled()) {
439: LOG.debug("Creating SharedFlowController of type "
440: + sharedFlowClass.getName());
441: }
442:
443: SharedFlowController retVal = (SharedFlowController) getFlowControllerInstance(sharedFlowClass);
444: HttpServletRequest request = context.getHttpRequest();
445: HttpServletResponse response = context.getHttpResponse();
446: retVal.create(request, response, getServletContext());
447:
448: if (LOG.isDebugEnabled()) {
449: LOG.debug("Storing new shared flow " + retVal
450: + " in the session");
451: }
452:
453: retVal.persistInSession(request, response);
454: return retVal;
455: }
456:
457: /**
458: * Get the map of shared flows for the given request. The map is derived from the shared flows
459: * that are declared (through the <code>sharedFlowRefs</code> attribute of
460: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller @Jpf.Controller}) in the
461: * page flow for the request.
462: *
463: * @param context a {@link RequestContext} object which contains the current request and response.
464: * @return a Map of shared-flow-name (String) to {@link SharedFlowController}.
465: * @throws ClassNotFoundException if a declared shared flow class could not be found.
466: * @throws InstantiationException if a declared shared flow class could not be instantiated.
467: * @throws IllegalAccessException if a declared shared flow class was not accessible.
468: */
469: public Map/*< String, SharedFlowController >*/getSharedFlowsForRequest(
470: RequestContext context) throws ClassNotFoundException,
471: InstantiationException, IllegalAccessException {
472: String path = InternalUtils.getDecodedServletPath(context
473: .getHttpRequest());
474: return getSharedFlowsForPath(context, path);
475: }
476:
477: /**
478: * Get the map of shared flows for the given path. The map is derived from the shared flows
479: * that are declared (through the <code>sharedFlowRefs</code> attribute of
480: * {@link org.apache.beehive.netui.pageflow.annotations.Jpf.Controller @Jpf.Controller}) in the page flow
481: * for the path.
482: *
483: * @param context a {@link RequestContext} object which contains the current request and response.
484: * @param path a <strong>webapp-relative</strong> path. The path should not contain the webapp context path.
485: * @return a Map of shared-flow-name (String) to {@link SharedFlowController}.
486: * @throws ClassNotFoundException if a declared shared flow class could not be found.
487: * @throws InstantiationException if a declared shared flow class could not be instantiated.
488: * @throws IllegalAccessException if a declared shared flow class was not accessible.
489: */
490: public Map/*< String, SharedFlowController >*/getSharedFlowsForPath(
491: RequestContext context, String path)
492: throws ClassNotFoundException, InstantiationException,
493: IllegalAccessException {
494: String parentDir = PageFlowUtils
495: .getModulePathForRelativeURI(path);
496: HttpServletRequest request = context.getHttpRequest();
497: HttpServletResponse response = context.getHttpResponse();
498: ModuleConfig mc = InternalUtils.ensureModuleConfig(parentDir,
499: getServletContext());
500: LinkedHashMap/*< String, SharedFlowController >*/sharedFlows = getDefaultSharedFlows(context);
501:
502: if (mc != null) {
503: ControllerConfig cc = mc.getControllerConfig();
504:
505: if (cc instanceof PageFlowControllerConfig) {
506: Map/*< String, String >*/sharedFlowTypes = ((PageFlowControllerConfig) cc)
507: .getSharedFlowTypes();
508:
509: if (sharedFlowTypes != null
510: && sharedFlowTypes.size() > 0) {
511: if (sharedFlows == null)
512: sharedFlows = new LinkedHashMap/*< String, SharedFlowController >*/();
513:
514: for (Iterator/*<Map.Entry>*/i = sharedFlowTypes
515: .entrySet().iterator(); i.hasNext();) {
516: Map.Entry entry = (Map.Entry) i.next();
517: String name = (String) entry.getKey();
518: String type = (String) entry.getValue();
519: addSharedFlow(context, name, type, sharedFlows);
520: }
521:
522: return sharedFlows;
523: }
524: }
525: }
526:
527: //
528: // Legacy behavior: if there's no shared flow for the request, initialize a GlobalApp instance.
529: //
530: SharedFlowController ga = PageFlowUtils.getGlobalApp(request);
531:
532: if (ga != null) {
533: ga.reinitialize(request, response, getServletContext());
534: } else {
535: getGlobalApp(request, response, getServletContext());
536: }
537:
538: return sharedFlows;
539: }
540:
541: /**
542: * Get a FlowController class. By default, this loads the class using the thread context class loader.
543: *
544: * @param className the name of the {@link FlowController} class to load.
545: * @return the loaded {@link FlowController} class.
546: * @throws ClassNotFoundException if the requested class could not be found.
547: */
548: public Class getFlowControllerClass(String className)
549: throws ClassNotFoundException {
550: return _rch.loadClass(className);
551: }
552:
553: /**
554: * Get a FlowController instance, given a FlowController class.
555: *
556: * @param flowControllerClass the Class, which must be assignable to {@link FlowController}.
557: * @return a new FlowController instance.
558: */
559: public FlowController getFlowControllerInstance(
560: Class flowControllerClass) throws InstantiationException,
561: IllegalAccessException {
562: assert FlowController.class
563: .isAssignableFrom(flowControllerClass) : "Class "
564: + flowControllerClass.getName() + " does not extend "
565: + FlowController.class.getName();
566: return (FlowController) flowControllerClass.newInstance();
567: }
568:
569: /* ------------------------------------------------------------------------------
570:
571: Deprecated APIs
572:
573: ------------------------------------------------------------------------------ */
574:
575: /**
576: * Get the page flow instance that should be associated with the given request. If it doesn't exist, create it.
577: * If one is created, the page flow stack (for nesting) will be cleared or pushed, and the new instance will be
578: * stored as the current page flow.
579: * @deprecated Use {@link #getPageFlowForRequest(RequestContext)} instead.
580: *
581: * @param request the current HttpServletRequest.
582: * @param response the current HttpServletResponse.
583: * @param servletContext the current ServletContext.
584: * @return the {@link PageFlowController} for the request, or <code>null</code> if none was found.
585: */
586: public static PageFlowController getPageFlowForRequest(
587: HttpServletRequest request, HttpServletResponse response,
588: ServletContext servletContext) {
589: return getPageFlowForRelativeURI(request, response,
590: InternalUtils.getDecodedServletPath(request),
591: servletContext);
592: }
593:
594: /**
595: * Get the page flow instance that should be associated with the given URI. If it doesn't exist, create it.
596: * If one is created, the page flow stack (for nesting) will be cleared or pushed, and the new instance will be
597: * stored as the current page flow.
598: * @deprecated Use {@link #getPageFlowForPath(RequestContext, String)} instead. The URI must be stripped of the
599: * webapp context path before being passed.
600: *
601: * @param request the current HttpServletRequest.
602: * @param response the current HttpServletResponse.
603: * @param uri a server-relative URI. The URI should contain the webapp context path.
604: * @param servletContext the current ServletContext.
605: * @return the {@link PageFlowController} for the given URI, or <code>null</code> if none was found.
606: */
607: public static PageFlowController getPageFlowForURI(
608: HttpServletRequest request, HttpServletResponse response,
609: String uri, ServletContext servletContext) {
610: return getPageFlowForRelativeURI(request, response,
611: PageFlowUtils.getRelativeURI(request, uri, null),
612: servletContext);
613: }
614:
615: /**
616: * Get the page flow instance that should be associated with the given path. If it doesn't exist, create it.
617: * If one is created, the page flow stack (for nesting) will be cleared or pushed, and the new instance will be
618: * stored as the current page flow.
619: * @deprecated Use {@link #getPageFlowForPath(RequestContext, String)} instead.
620: *
621: * @param request the current HttpServletRequest.
622: * @param response the current HttpServletResponse.
623: * @param path a <strong>webapp-relative</strong> path. The path should not contain the webapp context path.
624: * @param servletContext the current ServletContext.
625: * @return the {@link PageFlowController} for the given path, or <code>null</code> if none was found.
626: */
627: public static PageFlowController getPageFlowForRelativeURI(
628: HttpServletRequest request, HttpServletResponse response,
629: String path, ServletContext servletContext) {
630: try {
631: return get(servletContext).getPageFlowForPath(
632: new RequestContext(request, response), path);
633: } catch (InstantiationException e) {
634: LOG.error(
635: "Could not instantiate PageFlowController for request "
636: + request.getRequestURI(), e);
637: return null;
638: } catch (IllegalAccessException e) {
639: LOG.error(
640: "Could not instantiate PageFlowController for request "
641: + request.getRequestURI(), e);
642: return null;
643: }
644: }
645:
646: /**
647: * Create a page flow of the given type. The page flow stack (for nesting) will be cleared or pushed, and the new
648: * instance will be stored as the current page flow.
649: * @deprecated Use {@link #createPageFlow(RequestContext, String)} instead.
650: *
651: * @param request the current HttpServletRequest.
652: * @param response the current HttpServletResponse.
653: * @param pageFlowClassName the type name of the desired page flow.
654: * @param servletContext the current ServletContext.
655: * @return the newly-created {@link PageFlowController}, or <code>null</code> if none was found.
656: */
657: public static PageFlowController getPageFlow(
658: String pageFlowClassName, HttpServletRequest request,
659: HttpServletResponse response, ServletContext servletContext) {
660: try {
661: return get(servletContext).createPageFlow(
662: new RequestContext(request, response),
663: pageFlowClassName);
664: } catch (ClassNotFoundException e) {
665: LOG.error("Requested page flow class " + pageFlowClassName
666: + " not found.");
667: return null;
668: } catch (InstantiationException e) {
669: LOG.error(
670: "Could not instantiate PageFlowController of type "
671: + pageFlowClassName, e);
672: return null;
673: } catch (IllegalAccessException e) {
674: LOG.error(
675: "Could not instantiate PageFlowController of type "
676: + pageFlowClassName, e);
677: return null;
678: }
679: }
680:
681: /**
682: * Get or create the current user session's GlobalApp instance (from the Global.app file).
683: * @deprecated Global.app is deprecated; use shared flows and {@link #getSharedFlowsForRequest(RequestContext)}.
684: *
685: * @param request the current HttpServletRequest.
686: * @param response the current HttpServletResponse.
687: * @return the current session's {@link GlobalApp} instance, or a new one (based on Global.app) if none is found.
688: * If Global.app does not exist in the current webapp, <code>null</code> is returned.
689: */
690: public static GlobalApp getGlobalApp(HttpServletRequest request,
691: HttpServletResponse response, ServletContext servletContext) {
692: Boolean noGlobalApp = (Boolean) servletContext
693: .getAttribute(NO_GLOBAL_APP_KEY);
694: if (noGlobalApp != null && noGlobalApp.booleanValue())
695: return null;
696:
697: GlobalApp current = PageFlowUtils.getGlobalApp(request);
698: if (current != null)
699: return current;
700:
701: try {
702: try {
703: FlowControllerFactory factory = get(servletContext);
704: SharedFlowController sf = factory.createSharedFlow(
705: new RequestContext(request, response),
706: PageFlowConstants.GLOBALAPP_CLASSNAME);
707:
708: if (!(sf instanceof GlobalApp)) {
709: LOG.error("Class "
710: + PageFlowConstants.GLOBALAPP_CLASSNAME
711: + " is not an instance of "
712: + GlobalApp.class.getName());
713: return null;
714: }
715:
716: return (GlobalApp) sf;
717: } catch (InstantiationException e) {
718: LOG.error("Could not instantiate Global.app.", e);
719: return null;
720: } catch (IllegalAccessException e) {
721: LOG.error("Could not instantiate Global.app", e);
722: return null;
723: }
724: }
725: /*
726: This is an expected failure. When no "global/Global.class" is found in classloader, the
727: ReloadableClassHandler will throw a CNF exception. When that happens, set a flag that
728: short-circuits this check on subsequent requests so as to prevent throwing CNF(s) for
729: every request.
730:
731: Note, iterative development of "global/Global.class" files doesn't work in "bouncy" mode
732: and shouldn't -- this is a deprecated feature and just needs to continue to function.
733: */
734: catch (ClassNotFoundException e) {
735: LOG.info("No Global.app was found in "
736: + request.getContextPath());
737: servletContext
738: .setAttribute(NO_GLOBAL_APP_KEY, Boolean.TRUE);
739: return null;
740: }
741: }
742:
743: /**
744: * Create a page flow of the given type. The page flow stack (for nesting) will be cleared or pushed, and the new
745: * instance will be stored as the current page flow.
746: * @deprecated Use {@link #createPageFlow(RequestContext, Class)} instead.
747: *
748: * @param request the current HttpServletRequest.
749: * @param response the current HttpServletResponse.
750: * @param pageFlowClass the type of the desired page flow.
751: * @param servletContext the current ServletContext.
752: * @return the newly-created {@link PageFlowController}, or <code>null</code> if none was found.
753: */
754: public static PageFlowController getPageFlow(Class pageFlowClass,
755: HttpServletRequest request, HttpServletResponse response,
756: ServletContext servletContext) {
757: try {
758: FlowControllerFactory factory = get(servletContext);
759: return factory.createPageFlow(new RequestContext(request,
760: response), pageFlowClass);
761: } catch (InstantiationException e) {
762: LOG.error(
763: "Could not instantiate PageFlowController of type "
764: + pageFlowClass.getName(), e);
765: return null;
766: } catch (IllegalAccessException e) {
767: LOG.error(
768: "Could not instantiate PageFlowController of type "
769: + pageFlowClass.getName(), e);
770: return null;
771: }
772: }
773:
774: /* ------------------------------------------------------------------------------
775:
776: Internal APIs
777:
778: ------------------------------------------------------------------------------ */
779:
780: LinkedHashMap/*< String, SharedFlowController >*/getDefaultSharedFlows(
781: RequestContext context) throws ClassNotFoundException,
782: InstantiationException, IllegalAccessException {
783: SharedFlowRefConfig[] defaultRefs = ConfigUtil.getConfig()
784: .getSharedFlowRefs();
785:
786: if (defaultRefs != null) {
787: if (defaultRefs.length > 0) {
788: LinkedHashMap/*< String, SharedFlowController >*/sharedFlows = new LinkedHashMap();
789:
790: for (int i = 0; i < defaultRefs.length; i++) {
791: SharedFlowRefConfig ref = defaultRefs[i];
792: if (LOG.isInfoEnabled()) {
793: LOG
794: .info("Shared flow of type "
795: + ref.getType()
796: + " is a default shared flow reference "
797: + "with name " + ref.getName());
798: }
799: addSharedFlow(context, ref.getName(),
800: ref.getType(), sharedFlows);
801: }
802:
803: return sharedFlows;
804: }
805: }
806:
807: return null;
808: }
809:
810: private void addSharedFlow(RequestContext context, String name,
811: String type, LinkedHashMap sharedFlows)
812: throws ClassNotFoundException, InstantiationException,
813: IllegalAccessException {
814: HttpServletRequest request = context.getHttpRequest();
815: SharedFlowController sf = PageFlowUtils.getSharedFlow(type,
816: request);
817:
818: //
819: // Reinitialize transient data that may have been lost on session failover.
820: //
821: if (sf != null) {
822: sf.reinitialize(request, context.getHttpResponse(),
823: getServletContext());
824: } else {
825: sf = createSharedFlow(context, type);
826: }
827:
828: if (!(sf instanceof GlobalApp))
829: sharedFlows.put(name, sf);
830: }
831: }
|