You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by mr...@apache.org on 2005/08/26 07:46:58 UTC

svn commit: r240168 [3/30] - in /struts/sandbox/trunk/ti: ./ core/src/java/org/apache/ti/ core/src/java/org/apache/ti/config/ core/src/java/org/apache/ti/config/mapper/ core/src/java/org/apache/ti/core/ core/src/java/org/apache/ti/core/factory/ core/sr...

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/core/urltemplates/URLTemplatesFactory.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/core/urltemplates/URLTemplatesFactory.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/core/urltemplates/URLTemplatesFactory.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/core/urltemplates/URLTemplatesFactory.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.core.urltemplates;
+
+import org.apache.commons.chain.web.WebContext;
+import org.apache.ti.core.factory.Factory;
+import org.apache.ti.core.urls.TemplatedURLFormatter;
+import org.apache.ti.pageflow.ContainerAdapter;
+import org.apache.ti.pageflow.xwork.PageFlowActionContext;
+import org.apache.ti.util.SourceResolver;
+
+import java.util.Collection;
+
+/**
+ * Access point to URL templates (an optional config file to help format
+ * rewritten URLs) used by a {@link org.apache.ti.core.urls.TemplatedURLFormatter}
+ * via the {@link org.apache.ti.core.urls.URLRewriterService}.
+ */
+public abstract class URLTemplatesFactory extends Factory {
+
+    private static final String URL_TEMPLATE_FACTORY_ATTR = "_netui:urlTemplatesFactory";
+
+    // Constants for URL template types
+    public static final String DEFAULT_TEMPLATE = "default";
+    public static final String SECURE_DEFAULT_TEMPLATE = "secure-default";
+    public static final String ACTION_TEMPLATE = "action";
+    public static final String SECURE_ACTION_TEMPLATE = "secure-action";
+    public static final String RESOURCE_TEMPLATE = "resource";
+    public static final String SECURE_RESOURCE_TEMPLATE = "secure-resource";
+    public static final String RENDER_TEMPLATE = "render";
+    public static final String SECURE_RENDER_TEMPLATE = "secure-render";
+
+    /**
+     * Default value for path from the web app to the URL templates.
+     */
+    public static final String DEFAULT_URL_TEMPLATE_CONFIG_FILE_PATH = "/WEB-INF/beehive-url-template-config.xml";
+
+    // Path to the URL templates config file.
+    private String _configFilePath = DEFAULT_URL_TEMPLATE_CONFIG_FILE_PATH;
+
+    // The known tokens (collection of String objects) in a valid template.
+    private Collection _knownTokens = null;
+
+    // The required tokens (collection of String objects) in a valid template.
+    private Collection _requiredTokens = null;
+
+    /**
+     * Gets the URLTemplatesFactory instance attribute of the application.
+     *
+     * @return the URLTemplatesFactory instance from the application scope.
+     */
+    public static URLTemplatesFactory getURLTemplatesFactory() {
+        return (URLTemplatesFactory)
+                PageFlowActionContext.get().getApplication().get(URL_TEMPLATE_FACTORY_ATTR);
+    }
+
+    /**
+     * Adds a given URLTemplatesFactory instance as an attribute of the application.
+     */
+    public static void initApplication(WebContext webContext, URLTemplatesFactory defaultFactory,
+                                       TemplatedURLFormatter formatter, ContainerAdapter containerAdapter,
+                                       SourceResolver sourceResolver) {
+        // URLTemplatesFactory has not been initialized,
+        // get a URLTemplatesFactory object from the containerAdapter.
+        URLTemplatesFactory templatesFactory = createURLTemplatesFactory(defaultFactory, containerAdapter);
+        
+        // get the known/req tokens from the default formatter for the factory to use to verify templates
+        templatesFactory.setKnownTokens(formatter.getKnownTokens());
+        templatesFactory.setRequiredTokens(formatter.getRequiredTokens());
+        templatesFactory.load(webContext, sourceResolver);
+        webContext.getApplicationScope().put(URL_TEMPLATE_FACTORY_ATTR, templatesFactory);
+    }
+
+    /**
+     * Get an uninitialized instance of a container specific URLTemplatesFactory
+     * from the ContainerAdapter. If none exists, this returns an instance of a given default.
+     * Caller should then set the known
+     * and required tokens, call the {@link URLTemplatesFactory#load}
+     * method and {@link URLTemplatesFactory#initApplication}.
+     * <p/>
+     * <p/>
+     * IMPORTANT NOTE - Always try to get the application instance from the ServletContext
+     * by calling {@link URLTemplatesFactory#getURLTemplatesFactory}.
+     * Then, if a new URLTemplatesFactory must be created, call this method.
+     * </p>
+     *
+     * @return a container specific implementation of URLTemplatesFactory, or
+     *         {@link org.apache.ti.pageflow.internal.DefaultURLTemplatesFactory}.
+     */
+    private static URLTemplatesFactory createURLTemplatesFactory(URLTemplatesFactory defaultFactory,
+                                                                 ContainerAdapter containerAdapter) {
+        // get the URLTemplatesFactory from the containerAdapter.
+        URLTemplatesFactory factory = (URLTemplatesFactory) containerAdapter.getFactory(URLTemplatesFactory.class, null, null);
+
+        // if there's no URLTemplatesFactory, use our default impl.
+        return factory != null ? factory : defaultFactory;
+    }
+
+    /**
+     * Allow clients to set their own URL template config file name/path.
+     *
+     * @param configFilePath An absolute path from the web app root to the URL template config file.
+     */
+    public void setConfigFilePath(String configFilePath) {
+        if (configFilePath == null) {
+            throw new IllegalArgumentException("Config file path cannot be null.");
+        }
+
+        _configFilePath = configFilePath;
+    }
+
+    /**
+     * Allow clients to define a set of known tokens for the
+     * template verification. Tokens are expected to be qualified
+     * in braces. E.g. {url:path}
+     * <p/>
+     * The template verification will ensure the known tokens in the
+     * URL template conforms to a valid format.
+     *
+     * @param knownTokens The set of known tokens for a valid template.
+     */
+    public void setKnownTokens(Collection knownTokens) {
+        _knownTokens = knownTokens;
+    }
+
+    /**
+     * Allow clients to define a set of required tokens for the
+     * template verification. Tokens are expected to be qualified
+     * in braces. E.g. {url:path}
+     * <p/>
+     * The template verification will ensure the URL template conforms to
+     * a valid format for known tokens and contains the required tokens.
+     * </p>
+     *
+     * @param requiredTokens The set of required tokens in a valid template.
+     */
+    public void setRequiredTokens(Collection requiredTokens) {
+        _requiredTokens = requiredTokens;
+    }
+
+    /**
+     * Returns URL template given the name of the template.
+     *
+     * @param name name of the template
+     * @return template
+     */
+    public abstract URLTemplate getURLTemplate(String name);
+
+    /**
+     * Returns URL template name of the given type (by key) from the
+     * desired reference group.
+     *
+     * @param refGroupName name of a group of templates from the config file.
+     * @param key          type of the template
+     * @return template name
+     */
+    public abstract String getTemplateNameByRef(String refGroupName, String key);
+
+    /**
+     * Initialization method that parses the URL template config file to
+     * get the URL templates and template reference groups.
+     */
+    protected abstract void load(WebContext webContext, SourceResolver sourceResolver);
+
+    protected String getConfigFilePath() {
+        return _configFilePath;
+    }
+
+    protected Collection getKnownTokens() {
+        return _knownTokens;
+    }
+
+    protected Collection getRequiredTokens() {
+        return _requiredTokens;
+    }
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ActionNotFoundException.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ActionNotFoundException.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ActionNotFoundException.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ActionNotFoundException.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import org.apache.ti.pageflow.xwork.PageFlowActionContext;
+
+
+/**
+ * Exception that occurs when the user tries to execute an action that does not exist on the page flow.
+ */
+public class ActionNotFoundException extends FlowControllerException {
+
+    private Object _form;
+
+
+    public ActionNotFoundException(FlowController fc) {
+        super(fc);
+        _form = PageFlowActionContext.get().getFormBean();
+    }
+
+    protected Object[] getMessageArgs() {
+        return new Object[]{getActionName(), getFlowControllerURI(), _form != null ? _form.getClass().getName() : null};
+    }
+
+    protected String[] getMessageParts() {
+        String formDescrip = _form != null ? "(form " + _form.getClass().getName() + ") " : "";
+        return new String[]{"Unable to find action ", " (form=", ") in Page Flow ", "."};
+    }
+
+    protected Object getForm() {
+        return _form;
+    }
+
+    /**
+     * Tell whether the root cause may be session expiration in cases where the requested session ID is different than
+     * the actual session ID.  In this case, the answer is <code>false</code>.
+     */
+    public boolean causeMayBeSessionExpiration() {
+        return false;
+    }
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ActionResolver.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ActionResolver.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ActionResolver.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ActionResolver.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+
+/**
+ * Interface for controller classes that resolve actions to URIs.
+ */
+public interface ActionResolver {
+
+    /**
+     * Get the path for addressing this object within an application.
+     */
+    public String getPath();
+
+    /**
+     * Get the namespace associated with this ActionResolver.
+     */
+    public String getNamespace();
+
+    /**
+     * Get the ModuleConfig associated with this ActionResolver.
+     */
+    public ModuleConfig getModuleConfig();
+
+    /**
+     * Called on this object for non-lookup (refresh) requests.
+     */
+    public void refresh();
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ActionResult.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ActionResult.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ActionResult.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ActionResult.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import java.io.PrintWriter;
+
+/**
+ * Encapsulation of the results returned by {@link PageFlowUtils#strutsLookup}.
+ */
+public interface ActionResult {
+
+    public String getURI();
+
+    public boolean isRedirect();
+
+    public boolean isError();
+
+    public int getStatusCode();
+
+    public String getStatusMessage();
+
+    /**
+     * @deprecated This method now always returns false; compilation no longer happens at runtime.  It will be removed
+     *             in the next major release.
+     */
+    public boolean hadCompileErrors();
+
+    /**
+     * @deprecated This method has no effect; compilation no longer happens at runtime.  It will be removed in the next
+     *             major release.
+     */
+    public void printCompileErrors(PrintWriter writer);
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/AutoRegisterActionServlet.java.disabled
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/AutoRegisterActionServlet.java.disabled?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/AutoRegisterActionServlet.java.disabled (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/AutoRegisterActionServlet.java.disabled Thu Aug 25 22:46:03 2005
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import org.apache.ti.util.internal.InternalStringBuilder;
+
+import org.apache.ti.pageflow.internal.InternalConstants;
+import org.apache.ti.pageflow.internal.InternalUtils;
+import org.apache.ti.pageflow.internal.AdapterManager;
+import org.apache.ti.pageflow.xwork.ModuleConfig;
+import org.apache.ti.util.Bundle;
+import org.apache.ti.util.internal.DiscoveryUtils;
+import org.apache.ti.util.config.ConfigUtil;
+import org.apache.ti.util.config.bean.ModuleConfigLocators;
+import org.apache.ti.util.config.bean.PageflowConfig;
+import org.apache.ti.util.logging.Logger;
+import org.apache.commons.digester.Digester;
+import org.apache.struts.Globals;
+import org.apache.struts.action.ActionServlet;
+import org.apache.struts.action.DynaActionFormClass;
+import org.apache.struts.action.RequestProcessor;
+import org.apache.struts.config.ControllerConfig;
+import org.apache.struts.config.FormBeanConfig;
+import org.apache.struts.config.MessageResourcesConfig;
+import org.apache.struts.config.ModuleConfig;
+import org.apache.struts.config.ModuleConfigFactory;
+import org.apache.struts.config.impl.ModuleConfigImpl;
+import org.apache.struts.util.RequestUtils;
+import org.xml.sax.InputSource;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Iterator;
+
+import org.apache.ti.util.internal.concurrent.InternalConcurrentHashMap;
+
+
+/**
+ * ActionServlet that automatically registers requested Struts modules based on a set of module configuration file
+ * locators.  The user may specify {@link ModuleConfigLocator} classes in /WEB-INF/beehive-netui-config.xml using the
+ * <code>&lt;module-config-locators&gt;</code> element.
+ */
+public class AutoRegisterActionServlet extends ActionServlet
+{
+
+    public void init()
+        throws ServletException
+    {
+    }
+    
+    /**
+     * This override of the base class process() registers a Struts module on the fly if the
+     * config file can be found in our standard place (named in our standard way), regardless
+     * of whether the module is configured in web.xml.
+     */
+    protected void process( HttpServletRequest request, HttpServletResponse response )
+        throws IOException, ServletException
+    {
+        //
+        // First wrap the request with an object that contains request-scoped values that our runtime uses.  This
+        // is faster than sticking everything into attributes on the request (then basically reading from a HashMap).
+        //
+        PageFlowRequestWrapper requestWrapper = PageFlowRequestWrapper.wrapRequest( request );
+        request = requestWrapper;
+        
+        ServletContext servletContext = getServletContext();
+        String modulePath = PageFlowUtils.getModulePathForRequestPath( InternalUtils.getRequestPath( request ) );
+        ModuleConfig registeredApp;
+        
+        //
+        // Get the registered Struts module for the request.
+        //
+        registeredApp = getModuleConfig( modulePath, request, response );
+        
+        //
+        // If we've dynamically registered a module, then we need to override the base process() behavior to select the
+        // module.  Note that we don't want to synchronize the call to process().
+        //
+        if ( registeredApp != null )
+        {
+            //
+            // Try to select the appropriate Struts module and delegate to its RequestProcessor.
+            //
+            ModuleConfig moduleConfig = InternalUtils.selectModule( modulePath, request, servletContext );
+            RequestProcessor requestProcessor = getRequestProcessor( moduleConfig );
+            requestProcessor.process( request, response );
+        }
+        else
+        {
+            
+            //
+            // This is the same as the base process() behavior, but it checks for a missing module-configuration.
+            //
+            ModuleConfig moduleConfig = null;
+            
+            if ( InternalUtils.getModuleConfig( RequestUtils.getModuleName( request, servletContext ), servletContext ) != null )
+            {
+                String modulePrefix = RequestUtils.getModuleName( request, servletContext );
+                moduleConfig = InternalUtils.selectModule( modulePrefix, request, servletContext );
+            }
+            
+            String servletPath = InternalUtils.getRequestPath( request );
+            RequestProcessor rp = moduleConfig != null ? getRequestProcessor( moduleConfig ) : null;
+            
+            if ( rp != null && moduleCanHandlePath( moduleConfig, rp, servletPath ) )
+            {
+                rp.process( request, response );
+            }
+            else
+            {
+                //
+                // Initialize the ServletContext in the request.  Often, we need access to the ServletContext when we only
+                // have a ServletRequest.
+                //
+                InternalUtils.setServletContext( request, getServletContext() );
+                
+                if ( processUnhandledAction( request, response, servletPath ) ) return;
+                
+                String originalServletPath = requestWrapper.getOriginalServletPath();
+                if ( originalServletPath != null )
+                {
+                    servletPath = originalServletPath;
+                    modulePath = PageFlowUtils.getModulePathForRequestPath( originalServletPath );
+                }
+                
+                if ( _log.isErrorEnabled() )
+                {
+                    InternalStringBuilder msg = new InternalStringBuilder( "No module configuration registered for " );
+                    msg.append( servletPath ).append( " (module path " ).append( modulePath ).append( ")." );
+                    _log.error( msg.toString() );
+                }
+
+                //
+                // If we're not in production mode, send a diagnostic on the response; otherwise, simply send a 404.
+                //
+                if ( modulePath.length() == 0 ) modulePath = "/";
+                InternalUtils.sendDevTimeError( "PageFlow_NoModuleConf", null, HttpServletResponse.SC_NOT_FOUND, 
+                                                request, response, servletContext,
+                                                new Object[]{ servletPath, modulePath } );
+            }
+        }
+    }
+ 
+    public void destroy()
+    {
+        super.destroy();
+    }
+
+    /**
+     * Last chance to handle an unhandled action URI.
+     * @return <code>true</code> if this method handled it (by forwarding somewhere or writing to the response).
+     */ 
+    protected boolean processUnhandledAction( HttpServletRequest request, HttpServletResponse response, String uri )
+            throws IOException, ServletException
+    {
+        return false;
+    }
+}
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import org.apache.ti.util.internal.InternalStringBuilder;
+
+import org.apache.ti.pageflow.internal.InternalConstants;
+import org.apache.ti.pageflow.internal.InternalUtils;
+import org.apache.ti.pageflow.internal.AdapterManager;
+import org.apache.ti.pageflow.xwork.ModuleConfig;
+import org.apache.ti.util.Bundle;
+import org.apache.ti.util.internal.DiscoveryUtils;
+import org.apache.ti.util.config.ConfigUtil;
+import org.apache.ti.util.config.bean.ModuleConfigLocators;
+import org.apache.ti.util.config.bean.PageflowConfig;
+import org.apache.ti.util.logging.Logger;
+import org.apache.commons.digester.Digester;
+import org.apache.struts.Globals;
+import org.apache.struts.action.ActionServlet;
+import org.apache.struts.action.DynaActionFormClass;
+import org.apache.struts.action.RequestProcessor;
+import org.apache.struts.config.ControllerConfig;
+import org.apache.struts.config.FormBeanConfig;
+import org.apache.struts.config.MessageResourcesConfig;
+import org.apache.struts.config.ModuleConfig;
+import org.apache.struts.config.ModuleConfigFactory;
+import org.apache.struts.config.impl.ModuleConfigImpl;
+import org.apache.struts.util.RequestUtils;
+import org.xml.sax.InputSource;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.UnavailableException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Iterator;
+
+import org.apache.ti.util.internal.concurrent.InternalConcurrentHashMap;
+
+
+/**
+ * ActionServlet that automatically registers requested Struts modules based on a set of module configuration file
+ * locators.  The user may specify {@link ModuleConfigLocator} classes in /WEB-INF/beehive-netui-config.xml using the
+ * <code>&lt;module-config-locators&gt;</code> element.
+ */
+public class AutoRegisterActionServlet extends ActionServlet
+{
+
+    public void init()
+        throws ServletException
+    {
+    }
+    
+    /**
+     * This override of the base class process() registers a Struts module on the fly if the
+     * config file can be found in our standard place (named in our standard way), regardless
+     * of whether the module is configured in web.xml.
+     */
+    protected void process( HttpServletRequest request, HttpServletResponse response )
+        throws IOException, ServletException
+    {
+        //
+        // First wrap the request with an object that contains request-scoped values that our runtime uses.  This
+        // is faster than sticking everything into attributes on the request (then basically reading from a HashMap).
+        //
+        PageFlowRequestWrapper requestWrapper = PageFlowRequestWrapper.wrapRequest( request );
+        request = requestWrapper;
+        
+        ServletContext servletContext = getServletContext();
+        String modulePath = PageFlowUtils.getModulePathForRequestPath( InternalUtils.getRequestPath( request ) );
+        ModuleConfig registeredApp;
+        
+        //
+        // Get the registered Struts module for the request.
+        //
+        registeredApp = getModuleConfig( modulePath, request, response );
+        
+        //
+        // If we've dynamically registered a module, then we need to override the base process() behavior to select the
+        // module.  Note that we don't want to synchronize the call to process().
+        //
+        if ( registeredApp != null )
+        {
+            //
+            // Try to select the appropriate Struts module and delegate to its RequestProcessor.
+            //
+            ModuleConfig moduleConfig = InternalUtils.selectModule( modulePath, request, servletContext );
+            RequestProcessor requestProcessor = getRequestProcessor( moduleConfig );
+            requestProcessor.process( request, response );
+        }
+        else
+        {
+            
+            //
+            // This is the same as the base process() behavior, but it checks for a missing module-configuration.
+            //
+            ModuleConfig moduleConfig = null;
+            
+            if ( InternalUtils.getModuleConfig( RequestUtils.getModuleName( request, servletContext ), servletContext ) != null )
+            {
+                String modulePrefix = RequestUtils.getModuleName( request, servletContext );
+                moduleConfig = InternalUtils.selectModule( modulePrefix, request, servletContext );
+            }
+            
+            String servletPath = InternalUtils.getRequestPath( request );
+            RequestProcessor rp = moduleConfig != null ? getRequestProcessor( moduleConfig ) : null;
+            
+            if ( rp != null && moduleCanHandlePath( moduleConfig, rp, servletPath ) )
+            {
+                rp.process( request, response );
+            }
+            else
+            {
+                //
+                // Initialize the ServletContext in the request.  Often, we need access to the ServletContext when we only
+                // have a ServletRequest.
+                //
+                InternalUtils.setServletContext( request, getServletContext() );
+                
+                if ( processUnhandledAction( request, response, servletPath ) ) return;
+                
+                String originalServletPath = requestWrapper.getOriginalServletPath();
+                if ( originalServletPath != null )
+                {
+                    servletPath = originalServletPath;
+                    modulePath = PageFlowUtils.getModulePathForRequestPath( originalServletPath );
+                }
+                
+                if ( _log.isErrorEnabled() )
+                {
+                    InternalStringBuilder msg = new InternalStringBuilder( "No module configuration registered for " );
+                    msg.append( servletPath ).append( " (module path " ).append( modulePath ).append( ")." );
+                    _log.error( msg.toString() );
+                }
+
+                //
+                // If we're not in production mode, send a diagnostic on the response; otherwise, simply send a 404.
+                //
+                if ( modulePath.length() == 0 ) modulePath = "/";
+                InternalUtils.sendDevTimeError( "PageFlow_NoModuleConf", null, HttpServletResponse.SC_NOT_FOUND, 
+                                                request, response, servletContext,
+                                                new Object[]{ servletPath, modulePath } );
+            }
+        }
+    }
+ 
+    public void destroy()
+    {
+        super.destroy();
+    }
+
+    /**
+     * Last chance to handle an unhandled action URI.
+     * @return <code>true</code> if this method handled it (by forwarding somewhere or writing to the response).
+     */ 
+    protected boolean processUnhandledAction( HttpServletRequest request, HttpServletResponse response, String uri )
+            throws IOException, ServletException
+    {
+        return false;
+    }
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ContainerAdapter.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ContainerAdapter.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ContainerAdapter.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ContainerAdapter.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import org.apache.ti.core.factory.Factory;
+import org.apache.ti.core.factory.FactoryConfig;
+import org.apache.ti.pageflow.adapter.Adapter;
+
+import javax.security.auth.login.LoginException;
+
+
+/**
+ * Adapter interface for plugging into various containers.  An implementor of this interface is "discovered" at
+ * runtime.  The discovery process is as follows:
+ * <ul>
+ * <li>
+ * A list of META-INF/services/org.apache.ti.pageflow.ContainerAdapter resources is obtained
+ * from classpath.  This means, for example, that a file called
+ * "org.apache.ti.pageflow.ContainerAdapter" under directory META-INF/services would be
+ * found inside any JAR file on classpath.
+ * </li>
+ * <li>
+ * Inside each of these resources is the name of a class that implements ContainerAdapter.  This class
+ * is loaded, and its {@link #accept accept} method is called.
+ * </li>
+ * <li>
+ * If {@link #accept accept} returns <code>true</code>, then that implementation class is chosen as the current
+ * adapter; otherwise, the next one in the list is tried.
+ * </li>
+ * <li>If no adapters are discovered, an instance of {@link DefaultContainerAdapter} is used.
+ * </ul>
+ */
+public interface ContainerAdapter
+        extends Adapter {
+
+    /**
+     * Tell whether the server is running in production mode.
+     *
+     * @return <code>true</code> if the server is running in production mode.
+     */
+    public boolean isInProductionMode();
+
+    /**
+     * Tell whether a web application resource requires a secure transport protocol.  This is
+     * determined from web.xml; for example, the following block specifies that all resources under
+     * /login require a secure transport protocol.
+     * <pre>
+     *    &lt;security-constraint&gt;
+     *        &lt;web-resource-collection&gt;
+     *          &lt;web-resource-name&gt;Secure PageFlow - begin&lt;/web-resource-name&gt;
+     *          &lt;url-pattern&gt;/login/*&lt;/url-pattern&gt;
+     *        &lt;/web-resource-collection&gt;
+     *        &lt;user-data-constraint&gt;
+     *           &lt;transport-guarantee&gt;CONFIDENTIAL&lt;/transport-guarantee&gt;
+     *        &lt;/user-data-constraint&gt;
+     *    &lt;/security-constraint&gt;
+     * </pre>
+     *
+     * @param path a webapp-relative path for a resource.
+     * @return <code>Boolean.TRUE</code> if a transport-guarantee of <code>CONFIDENTIAL</code> or
+     *         <code>INTEGRAL</code> is associated with the given resource; <code>Boolean.FALSE</code>
+     *         a transport-guarantee of <code>NONE</code> is associated with the given resource; or
+     *         <code>null</code> if there is no transport-guarantee associated with the given resource.
+     */
+    public SecurityProtocol getSecurityProtocol(String path);
+
+    /**
+     * Cause the server to do a security check for the given path.  If required, it does a redirect to
+     * change the scheme (http/https).
+     *
+     * @param path the webapp-relative path on which to run security checks.
+     * @return <code>true</code> if a redirect occurred.
+     */
+    boolean doSecurityRedirect(String path);
+
+
+    /**
+     * Get the port on which the server is listening for unsecure connections.
+     *
+     * @return the port on which the server is listening for unsecure connections.
+     */
+    public int getListenPort();
+
+    /**
+     * Get the port on which the server is listening for secure connections.
+     *
+     * @return the port on which the server is listening for secure connections.
+     */
+    public int getSecureListenPort();
+
+    /**
+     * Log in the user, using "weak" username/password authentication.
+     *
+     * @param username the user's login name.
+     * @param password the user's password.
+     * @throws LoginException if the authentication failed
+     */
+    public void login(String username, String password)
+            throws LoginException;
+
+    /**
+     * Log out the current user.
+     *
+     * @param invalidateSessions if <code>true</code>, the session is invalidated (on all single-signon webapps);
+     *                           otherwise the session and its data are left intact.  To invalidate the session in only the
+     *                           current webapp, set this parameter to <code>false</code> and call invalidate() on the HttpSession.
+     */
+    public void logout(boolean invalidateSessions);
+
+    /**
+     * Return the webapp context path for the given request.  This differs from HttpServletRequest.getContextPath()
+     * only in that it will return a valid value even if the request is for the default webapp.
+     */
+    public String getFullContextPath();
+
+    /**
+     * Ensure that the given session attribute is replicated in a cluster for session failover.
+     * This method does not need to be implemented for servers that do not support clustering and
+     * session failover.
+     *
+     * @param attrName the name of the session attribute for which failover should be ensured.
+     * @param attrVal  the value of the given session attribute.
+     */
+    public void ensureFailover(String attrName, Object attrVal);
+
+    /**
+     * Called at the beginning of each processed request.
+     */
+    public void beginRequest();
+
+    /**
+     * Called at the end of each processed request.
+     */
+    public void endRequest();
+
+    /**
+     * Get a context object to support Beehive Controls.
+     *
+     * @return a new ControlBeanContext.
+     */
+    public Object createControlBeanContext();
+
+    /**
+     * Get the name of the platform, which may be used to find platform-specific configuration files.
+     *
+     * @return the name of the platform
+     */
+    public String getPlatformName();
+
+    /**
+     * Get an event reporter, which will be notified of events like "page flow created", "action raised", etc.
+     */
+    public PageFlowEventReporter getEventReporter();
+
+    /**
+     * Generic method to get a Factory class that may be container dependent.
+     * <p/>
+     * <p/>
+     * This method is called to get the following Factory implementations:
+     * </p>
+     * <ul>
+     * <li>{@link org.apache.ti.core.urltemplates.URLTemplatesFactory}</li>
+     * </ul>
+     *
+     * @param classType the class type that the factory should extend or implement.
+     * @param id        can be used for the case where there is more than one possible Factory
+     *                  that extends or implaments the class type.
+     * @param config    a configuration object passed to a {@link org.apache.ti.core.factory.Factory}
+     * @return a Factory class that extends or implemtents the given class type.
+     */
+    public Factory getFactory(Class classType, String id, FactoryConfig config);
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ControlFieldInitializationException.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ControlFieldInitializationException.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ControlFieldInitializationException.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ControlFieldInitializationException.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+/**
+ * Exception thrown when there are errors initializing an annotated Control field.
+ */
+public class ControlFieldInitializationException
+        extends PageFlowManagedObjectException {
+
+    private String _fieldName;
+
+    /**
+     * Construct with no error message.
+     */
+    public ControlFieldInitializationException(String fieldName, PageFlowManagedObject object, Throwable cause) {
+        super(object, cause);
+        _fieldName = fieldName;
+    }
+
+    protected Object[] getMessageArgs() {
+        return new Object[]{_fieldName, getManagedObject().getDisplayName()};
+    }
+
+    protected String[] getMessageParts() {
+        return new String[]
+        {
+            "Exception occurred when initializing field ", " on page flow ", "."
+        };
+    }
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/DefaultContainerAdapter.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/DefaultContainerAdapter.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/DefaultContainerAdapter.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/DefaultContainerAdapter.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import org.apache.commons.chain.web.WebContext;
+import org.apache.ti.core.factory.Factory;
+import org.apache.ti.core.factory.FactoryConfig;
+import org.apache.ti.pageflow.internal.PageFlowBeanContext;
+import org.apache.ti.pageflow.xwork.PageFlowActionContext;
+import org.apache.ti.util.logging.Logger;
+
+import javax.security.auth.login.LoginException;
+
+
+/**
+ * Default implementation of a container adapter.
+ */
+public abstract class DefaultContainerAdapter
+        implements ContainerAdapter {
+
+    private static final Logger _log = Logger.getInstance(DefaultContainerAdapter.class);
+
+    private static boolean _productionMode = true;
+
+    private PageFlowEventReporter _eventReporter;
+
+    static {
+        String productionModeFlag = System.getProperty("beehive.productionmode");
+
+        if (productionModeFlag != null) {
+            _productionMode = Boolean.valueOf(productionModeFlag).booleanValue();
+        } else {
+            //
+            // This is our default definition of "production mode": when asserts are disabled (the following statement
+            // sets _productionMode to false when asserts are enabled).
+            //
+            assert (_productionMode = false) || true;
+        }
+    }
+
+    protected DefaultContainerAdapter() {
+    }
+
+    /**
+     * Tell whether the system is in production mode.
+     *
+     * @return <code>true</code> if the system property "beehive.productionmode" is set to "true", or if asserts are
+     *         disabled for this class in the case where the system property has no value; <code>false</code>  if the
+     *         system property is set to "false", or if asserts are enabled for this class in the case where the
+     *         system property has no value.
+     */
+    public boolean isInProductionMode() {
+        return _productionMode;
+    }
+
+    /**
+     * Tell whether a web application resource requires a secure transport protocol.  This default implementation
+     * simply returns {@link SecurityProtocol#UNSPECIFIED} for all paths.
+     *
+     * @param path a webapp-relative path for a resource.
+     * @return {@link SecurityProtocol#UNSPECIFIED}.
+     */
+    public SecurityProtocol getSecurityProtocol(String path) {
+        // TODO: implement this based on parsing of web.xml
+        return SecurityProtocol.UNSPECIFIED;
+    }
+
+    /**
+     * Cause the server to do a security check for the given path.  This default implementation does nothing.
+     *
+     * @return <code>false</code>
+     */
+    public boolean doSecurityRedirect(String path) {
+        return false;
+    }
+
+    /**
+     * Get the port on which the server is listening for unsecure connections.  This default implementation always
+     * returns <code>-1</code>.
+     *
+     * @return <code>-1</code>.
+     */
+    public int getListenPort() {
+        // TODO: have a configuration in netui-config.xml to specify this; an alternative to having to have an adapter.
+        return -1;
+    }
+
+    /**
+     * Get the port on which the server is listening for secure connections.  This default implementation always
+     * returns <code>-1</code>.
+     *
+     * @return <code>-1</code>.
+     */
+    public int getSecureListenPort() {
+        // TODO: have a configuration in netui-config.xml to specify this; an alternative to having to have an adapter.
+        return -1;
+    }
+
+    /**
+     * Log in the user, using "weak" username/password authentication.  This default implementation always throws
+     * {@link UnsupportedOperationException}.
+     *
+     * @throws UnsupportedOperationException in all cases.
+     */
+    public void login(String username, String password)
+            throws LoginException {
+        throw new UnsupportedOperationException("login is not supported by "
+                + DefaultContainerAdapter.class.getName());
+    }
+
+    /**
+     * Log out the user.  This default implementation always throws {@link UnsupportedOperationException}.
+     *
+     * @throws UnsupportedOperationException in all cases.
+     */
+    public void logout(boolean invalidateSessions) {
+        throw new UnsupportedOperationException("logout is not supported by "
+                + DefaultContainerAdapter.class.getName());
+    }
+
+    public String getFullContextPath() {
+        return PageFlowActionContext.get().getRequestContextPath();
+    }
+
+    /**
+     * Ensure that the given session attribute is replicated in a cluster for session failover.
+     * This method does not need to be implemented for servers that do not support clustering and
+     * session failover.  The default implementation does nothing.
+     *
+     * @param attrName the name of the session attribute for which failover should be ensured.
+     * @param attrVal  the value of the given session attribute.
+     */
+    public void ensureFailover(String attrName, Object attrVal) {
+    }
+
+    /**
+     * Called at the beginning of each processed request.  This default implementation does nothing.
+     */
+    public void beginRequest() {
+    }
+
+    /**
+     * Called at the end of each processed request.  This default implementation does nothing.
+     */
+    public void endRequest() {
+    }
+
+    /**
+     * Get a context object to support Beehive Controls.  This default implementation returns an instance of
+     * {@link PageFlowBeanContext}.
+     *
+     * @return a new ControlBeanContext.
+     */
+    public Object createControlBeanContext() {
+        return new PageFlowBeanContext();
+    }
+
+    /**
+     * Set the AdapterContext.
+     *
+     * @param context the AdapterContext to set.
+     */
+    public void initialize(WebContext context) {
+        _eventReporter = createEventReporter(context);
+    }
+
+    /**
+     * Get the name of the platform, which may be used to find platform-specific configuration files.  This default
+     * implementation returns "generic".
+     *
+     * @return the name of the platform ("generic" in this default implementation).
+     */
+    public String getPlatformName() {
+        return "generic";
+    }
+
+    /**
+     * Get an event reporter, which will be notified of events like "page flow created", "action raised", etc.
+     * This default implementation returns an instance of {@link DefaultPageFlowEventReporter}.
+     *
+     * @return a {@link PageFlowEventReporter}.
+     */
+    public PageFlowEventReporter getEventReporter() {
+        return _eventReporter;
+    }
+
+    protected PageFlowEventReporter createEventReporter(WebContext context) {
+        return new DefaultPageFlowEventReporter();
+    }
+
+    /**
+     * Generic method to get a Factory class that may be container dependent.
+     * <p/>
+     * <p/>
+     * This method is called to get the following Factory implementations:
+     * </p>
+     * <ul>
+     * <li>{@link org.apache.ti.core.urltemplates.URLTemplatesFactory}</li>
+     * </ul>
+     *
+     * @param factoryType the class type that the factory should extend or implement
+     * @param id          can be used for the case where there is more than one possible Factory
+     *                    that extends or implaments the class type.
+     * @param config      a configuration object passed to a {@link org.apache.ti.core.factory.Factory}
+     * @return a Factory class that extends or implemtents the given class type.
+     */
+    public Factory getFactory(Class factoryType, String id, FactoryConfig config) {
+        return null;
+    }
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/DefaultPageFlowEventReporter.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/DefaultPageFlowEventReporter.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/DefaultPageFlowEventReporter.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/DefaultPageFlowEventReporter.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import org.apache.ti.pageflow.xwork.PageFlowActionContext;
+import org.apache.ti.util.internal.InternalStringBuilder;
+import org.apache.ti.util.logging.Logger;
+
+/**
+ * Default event reporter.  Logs every event when the log level is set to "debug" or "trace".
+ */
+public class DefaultPageFlowEventReporter
+        extends PageFlowEventReporter {
+
+    private static final Logger _log = Logger.getInstance(DefaultPageFlowEventReporter.class);
+
+    protected DefaultPageFlowEventReporter() {
+        super();
+    }
+
+    public void actionRaised(FlowController flowController) {
+        if (_log.isDebugEnabled()) {
+            PageFlowActionContext context = PageFlowActionContext.get();
+            LogMsg msg = new LogMsg("actionRaised");
+            msg.addParam("FlowController", flowController);
+            msg.addParam("ActionName", context.getName());
+            msg.addParam("formBean", context.getFormBean());
+            msg.addParam("Request", context.getRequestPath());
+            _log.debug(msg);
+        }
+    }
+
+    public void actionSuccess(FlowController flowController, Forward result, long timeTakenMillis) {
+        if (_log.isDebugEnabled()) {
+            PageFlowActionContext context = PageFlowActionContext.get();
+            LogMsg msg = new LogMsg("actionSuccess");
+            msg.addParam("FlowController", flowController);
+            msg.addParam("ActionName", context.getName());
+            msg.addParam("formBean", context.getFormBean());
+            msg.addParam("Request", context.getRequestPath());
+            msg.addParam("forward", result);
+            msg.addParam("TimeTakenMillis", new Long(timeTakenMillis));
+            _log.debug(msg);
+        }
+    }
+
+    public void exceptionRaised(Throwable ex, FlowController flowController) {
+        if (_log.isDebugEnabled()) {
+            PageFlowActionContext context = PageFlowActionContext.get();
+            LogMsg msg = new LogMsg("exceptionRaised");
+            msg.addParam("Throwable", ex);
+            msg.addParam("ActionName", context.getName());
+            msg.addParam("formBean", context.getFormBean());
+            msg.addParam("FlowController", flowController);
+            msg.addParam("Request", context.getRequestPath());
+            _log.debug(msg);
+        }
+    }
+
+    public void exceptionHandled(Throwable ex, FlowController flowController, Forward result,
+                                 long timeTakenMillis) {
+        if (_log.isDebugEnabled()) {
+            PageFlowActionContext context = PageFlowActionContext.get();
+            LogMsg msg = new LogMsg("exceptionHandled");
+            msg.addParam("Throwable", ex);
+            msg.addParam("ActionName", context.getName());
+            msg.addParam("formBean", context.getFormBean());
+            msg.addParam("FlowController", flowController);
+            msg.addParam("Request", context.getRequestPath());
+            msg.addParam("forward", result);
+            msg.addParam("TimeTakenMillis", new Long(timeTakenMillis));
+            _log.debug(msg);
+        }
+    }
+
+    public void flowControllerCreated(FlowController flowController) {
+        if (_log.isDebugEnabled()) {
+            PageFlowActionContext context = PageFlowActionContext.get();
+            LogMsg msg = new LogMsg("flowControllerCreated");
+            msg.addParam("FlowController", flowController);
+            msg.addParam("Request", context.getRequestPath());
+            _log.debug(msg);
+        }
+    }
+
+    public void flowControllerDestroyed(FlowController flowController, Object storageLocation) {
+        if (_log.isDebugEnabled()) {
+            LogMsg msg = new LogMsg("flowControllerDestroyed");
+            msg.addParam("FlowController", flowController);
+            msg.addParam("StorageLocation", storageLocation);
+            _log.debug(msg);
+        }
+    }
+
+    public void beginActionRequest() {
+        if (_log.isDebugEnabled()) {
+            PageFlowActionContext context = PageFlowActionContext.get();
+            LogMsg msg = new LogMsg("beginActionRequest");
+            msg.addParam("Request", context.getRequestPath());
+            _log.debug(msg);
+        }
+    }
+
+    public void endActionRequest(long timeTakenMillis) {
+        if (_log.isDebugEnabled()) {
+            PageFlowActionContext context = PageFlowActionContext.get();
+            LogMsg msg = new LogMsg("endActionRequest");
+            msg.addParam("Request", context.getRequestPath());
+            msg.addParam("TimeTakenMillis", new Long(timeTakenMillis));
+            _log.debug(msg);
+        }
+    }
+
+    public void beginPageRequest() {
+        if (_log.isDebugEnabled()) {
+            PageFlowActionContext context = PageFlowActionContext.get();
+            LogMsg msg = new LogMsg("beginPageRequest");
+            msg.addParam("Request", context.getRequestPath());
+            _log.debug(msg);
+        }
+    }
+
+    public void endPageRequest(long timeTakenMillis) {
+        if (_log.isDebugEnabled()) {
+            PageFlowActionContext context = PageFlowActionContext.get();
+            LogMsg msg = new LogMsg("endPageRequest");
+            msg.addParam("Request", context.getRequestPath());
+            msg.addParam("TimeTakenMillis", new Long(timeTakenMillis));
+            _log.debug(msg);
+        }
+    }
+
+    public void flowControllerRegistered(ModuleConfig moduleConfig) {
+        if (_log.isDebugEnabled()) {
+            LogMsg msg = new LogMsg("flowControllerRegistered");
+            msg.addParam("Namespace", moduleConfig.getNamespace());
+            msg.addParam("ControllerClassName", moduleConfig.getControllerClassName());
+            msg.addParam("ModuleConfig", moduleConfig);
+            _log.debug(msg);
+        }
+    }
+
+    protected static class LogMsg {
+
+        private String _eventName;
+        private InternalStringBuilder _logMessage;
+
+        public LogMsg(String eventName) {
+            _eventName = eventName;
+        }
+
+        public void addParam(String name, Object value) {
+            if (_logMessage == null) {
+                _logMessage = new InternalStringBuilder(_eventName).append(": ");
+            } else {
+                _logMessage.append(", ");
+            }
+
+            _logMessage.append(name).append('=').append(value);
+        }
+
+        public String toString() {
+            return _logMessage == null ? _eventName : _logMessage.toString();
+        }
+    }
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/DoubleSubmitException.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/DoubleSubmitException.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/DoubleSubmitException.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/DoubleSubmitException.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Exception thrown when an action marked with the
+ * {@link org.apache.ti.pageflow.annotations.ti.action#preventDoubleSubmit &#64;ti.action(preventDoubleSubmit=...}
+ * annotation attribute has been submitted to more than once.
+ */
+public class DoubleSubmitException
+        extends FlowControllerException
+        implements ResponseErrorCodeSender {
+
+    DoubleSubmitException(FlowController fc) {
+        super(fc);
+    }
+
+    protected Object[] getMessageArgs() {
+        return new Object[]{getActionName(), getFlowControllerURI()};
+    }
+
+    protected String[] getMessageParts() {
+        return new String[]{"A double-submit occurred for action ", " in page flow ", "."};
+    }
+
+    public void sendResponseErrorCode(HttpServletResponse response) throws IOException {
+        response.sendError(HttpServletResponse.SC_BAD_REQUEST, getLocalizedMessage());
+    }
+
+    /**
+     * Tell whether the root cause may be session expiration in cases where the requested session ID is different than
+     * the actual session ID.  In this case, the answer is <code>true</code>.
+     */
+    public boolean causeMayBeSessionExpiration() {
+        return true;
+    }
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/EmptyNestingStackException.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/EmptyNestingStackException.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/EmptyNestingStackException.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/EmptyNestingStackException.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+
+/**
+ * Exception that occurs when the user invokes an action in a nested page flow that uses a
+ * <code>{@link org.apache.ti.pageflow.annotations.ti.forward &#64;ti.forward}(</code>...<code>
+ * returnAction="</code><i>action-name-in-calling-pageflow</i><code>")</code>
+ * annotation, but there is no calling page flow. This can happen in iterative
+ * development mode when you have modified files and caused the web application to be redeployed,
+ * or when the session expires.
+ */
+public class EmptyNestingStackException extends FlowControllerException {
+
+    public EmptyNestingStackException(FlowController fc) {
+        super(fc);
+    }
+
+    protected Object[] getMessageArgs() {
+        return new Object[]{getActionName(), getFlowControllerURI()};
+    }
+
+    protected String[] getMessageParts() {
+        return new String[]{"Empty nesting stack for returned action ", " from Page Flow ", "."};
+    }
+
+    /**
+     * Tell whether the root cause may be session expiration in cases where the requested session ID is different than
+     * the actual session ID.  In this case, the answer is <code>true</code>.
+     */
+    public boolean causeMayBeSessionExpiration() {
+        return true;
+    }
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ExpressionMessage.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ExpressionMessage.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ExpressionMessage.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/ExpressionMessage.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import org.apache.ti.core.ActionMessage;
+import org.apache.ti.pageflow.internal.InternalConstants;
+
+/**
+ * Extension of the base Struts ActionMessage; instead of retrieving messages and their arguments from message
+ * resources, it calculates them by evaluating JSP 2.0-style expressions (or, in the degenerate case, from hardcoded
+ * strings).
+ */
+public class ExpressionMessage
+        extends ActionMessage {
+
+    /**
+     * Constructor, using an array for the message arguments.
+     *
+     * @param expression            the JSP 2.0-style expression (e.g., <code>${pageFlow.myProperty}</code>) or literal string
+     *                              that will be used as the message.
+     * @param messageArgExpressions an array of JSP 2.0-style expressions <i>or</i> raw Objects to be used as arguments
+     *                              to the message.  Expressions are evaluated; all other Objects are passed as-is.
+     */
+    public ExpressionMessage(String expression, Object[] messageArgExpressions) {
+        super(InternalConstants.MESSAGE_IS_EXPRESSION_PREFIX + expression, prefixArgs(messageArgExpressions));
+    }
+
+    /**
+     * Constructor, for a message without message arguments.
+     *
+     * @param expression the JSP 2.0-style expression (e.g., <code>${pageFlow.myProperty}</code>) or literal string
+     *                   that will be used as the message.
+     */
+    public ExpressionMessage(String expression) {
+        this(expression, null);
+    }
+
+    /**
+     * Constructor, for a message with a single argument.
+     *
+     * @param expression           the JSP 2.0-style expression (e.g., <code>${pageFlow.myProperty}</code>) or literal string
+     *                             that will be used as the message.
+     * @param messageArgExpression a JSP 2.0-style expression <i>or</i> raw Object to be used the argument
+     *                             to the message.  Expressions are evaluated; all other Objects are passed as-is.
+     */
+    public ExpressionMessage(String expression, Object messageArgExpression) {
+        this(expression, new Object[]{messageArgExpression});
+    }
+
+    /**
+     * Constructor, for a message with two arguments.
+     *
+     * @param expression            the JSP 2.0-style expression (e.g., <code>${pageFlow.myProperty}</code>) or literal string
+     *                              that will be used as the message.
+     * @param messageArgExpression1 a JSP 2.0-style expression <i>or</i> raw Object to be used the first argument
+     *                              to the message.  Expressions are evaluated; all other Objects are passed as-is.
+     * @param messageArgExpression2 a JSP 2.0-style expression <i>or</i> raw Object to be used the second argument
+     *                              to the message.  Expressions are evaluated; all other Objects are passed as-is.
+     */
+    public ExpressionMessage(String expression, Object messageArgExpression1, Object messageArgExpression2) {
+        this(expression, new Object[]{messageArgExpression1, messageArgExpression2});
+    }
+
+    /**
+     * Constructor, for a message with two arguments.
+     *
+     * @param expression            the JSP 2.0-style expression (e.g., <code>${pageFlow.myProperty}</code>) or literal string
+     *                              that will be used as the message.
+     * @param messageArgExpression1 a JSP 2.0-style expression <i>or</i> raw Object to be used the first argument
+     *                              to the message.  Expressions are evaluated; all other Objects are passed as-is.
+     * @param messageArgExpression2 a JSP 2.0-style expression <i>or</i> raw Object to be used the second argument
+     *                              to the message.  Expressions are evaluated; all other Objects are passed as-is.
+     * @param messageArgExpression3 a JSP 2.0-style expression <i>or</i> raw Object to be used the third argument
+     *                              to the message.  Expressions are evaluated; all other Objects are passed as-is.
+     */
+    public ExpressionMessage(String expression, Object messageArgExpression1, Object messageArgExpression2,
+                             Object messageArgExpression3) {
+        this(expression, new Object[]{messageArgExpression1, messageArgExpression2, messageArgExpression3});
+    }
+
+    private static Object[] prefixArgs(Object[] messageArgExpressions) {
+        if (messageArgExpressions == null) return null;
+
+        Object[] ret = new Object[messageArgExpressions.length];
+
+        for (int i = 0; i < messageArgExpressions.length; i++) {
+            ret[i] = InternalConstants.MESSAGE_IS_EXPRESSION_PREFIX + messageArgExpressions[i];
+        }
+
+        return ret;
+    }
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/FacesBackingBean.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/FacesBackingBean.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/FacesBackingBean.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/FacesBackingBean.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import org.apache.ti.pageflow.handler.Handlers;
+import org.apache.ti.pageflow.handler.StorageHandler;
+import org.apache.ti.pageflow.internal.CachedFacesBackingInfo;
+import org.apache.ti.pageflow.internal.CachedSharedFlowRefInfo;
+import org.apache.ti.pageflow.internal.InternalConstants;
+import org.apache.ti.pageflow.internal.InternalUtils;
+import org.apache.ti.util.internal.cache.ClassLevelCache;
+import org.apache.ti.util.logging.Logger;
+
+import java.lang.reflect.Field;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * <p/>
+ * A JavaServer Faces backing bean.  An instance of this class will be created whenever a corresponding JSF path is
+ * requested (e.g., an instance of foo.MyPage will be created for the webapp-relative path "/foo/MyPage.faces").  The
+ * instance will be released (removed from the user session) when a non-matching path is requested.  A faces backing
+ * bean can hold component references and event/command handlers, and it can raise actions with normal JSF command event
+ * handlers that are annotated with {@link org.apache.ti.pageflow.annotations.ti.commandHandler &#64;ti.commandHandler}.
+ * The bean instance can be bound to with a JSF-style expression like <code>#{backing.myComponent}</code>.
+ * </p>
+ * <p/>
+ * JSF backing beans are configured using the
+ * {@link org.apache.ti.pageflow.annotations.ti.facesBacking &#64;ti.facesBacking} annotation.
+ * </p>
+ */
+public abstract class FacesBackingBean
+        extends PageFlowManagedObject {
+
+    private static final String CACHED_INFO_KEY = "cachedInfo";
+    private static final Logger _log = Logger.getInstance(FacesBackingBean.class);
+
+    private Map _pageInputs;
+
+
+    /**
+     * Store this object in the user session, in the appropriate place.  Used by the framework; normally should not be
+     * called directly.
+     */
+    public void persistInSession() {
+        StorageHandler sh = Handlers.get().getStorageHandler();
+        String attrName = InternalUtils.getScopedAttrName(InternalConstants.FACES_BACKING_ATTR);
+        sh.setAttribute(attrName, this);
+    }
+
+    /**
+     * Remove this instance from the session.
+     */
+    protected void removeFromSession() {
+        StorageHandler sh = Handlers.get().getStorageHandler();
+        String attrName = InternalUtils.getScopedAttrName(InternalConstants.FACES_BACKING_ATTR);
+
+        sh.removeAttribute(attrName);
+    }
+
+    /**
+     * Ensures that any changes to this object will be replicated in a cluster (for failover),
+     * even if the replication scheme uses a change-detection algorithm that relies on
+     * HttpSession.setAttribute to be aware of changes.  Note that this method is used by the framework
+     * and does not need to be called explicitly in most cases.
+     */
+    public void ensureFailover() {
+        StorageHandler sh = Handlers.get().getStorageHandler();
+        String attr = InternalUtils.getScopedAttrName(InternalConstants.FACES_BACKING_ATTR);
+        sh.ensureFailover(attr, this);
+    }
+
+    /**
+     * Get the display name for the bean.  For FacesBackingBeans, this is simply the class name.
+     */
+    public String getDisplayName() {
+        return getClass().getName();
+    }
+
+    /**
+     * Reinitialize the bean for a new request.  Used by the framework; normally should not be called directly.
+     */
+    public void reinitialize() {
+        super.reinitialize();
+
+        if (_pageInputs == null) {
+            Map map = InternalUtils.getActionOutputMap(false);
+            if (map != null) _pageInputs = Collections.unmodifiableMap(map);
+        }
+        
+        //
+        // Initialize the page flow field.
+        //
+        Field pageFlowMemberField = getCachedInfo().getPageFlowMemberField();
+        
+        // TODO: should we add a compiler warning if this field isn't transient?  All this reinitialization logic is
+        // for the transient case.
+        if (fieldIsUninitialized(pageFlowMemberField)) {
+            PageFlowController pfc = PageFlowUtils.getCurrentPageFlow();
+            initializeField(pageFlowMemberField, pfc);
+        }
+        
+        //
+        // Initialize the shared flow fields.
+        //
+        CachedSharedFlowRefInfo.SharedFlowFieldInfo[] sharedFlowMemberFields =
+                getCachedInfo().getSharedFlowMemberFields();
+
+        if (sharedFlowMemberFields != null) {
+            for (int i = 0; i < sharedFlowMemberFields.length; i++) {
+                CachedSharedFlowRefInfo.SharedFlowFieldInfo fi = sharedFlowMemberFields[i];
+                Field field = fi.field;
+
+                if (fieldIsUninitialized(field)) {
+                    Map/*< String, SharedFlowController >*/ sharedFlows = PageFlowUtils.getSharedFlows();
+                    String name = fi.sharedFlowName;
+                    SharedFlowController sf = name != null ? (SharedFlowController) sharedFlows.get(name) : null;
+
+                    if (sf != null) {
+                        initializeField(field, sf);
+                    } else {
+                        _log.error("Could not find shared flow with name \"" + fi.sharedFlowName
+                                + "\" to initialize field " + field.getName() + " in " + getClass().getName());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Get a page input that was passed from a Page Flow action as an "action output".
+     *
+     * @param pageInputName the name of the page input.  This is the same as the name of the "action output".
+     * @return the value of the page input, or <code>null</code> if the given one does not exist.
+     */
+    protected Object getPageInput(String pageInputName) {
+        return _pageInputs != null ? _pageInputs.get(pageInputName) : null;
+    }
+
+    /**
+     * Get the map of all page inputs that was passed from a Page Flow action as "action outputs".
+     *
+     * @return a Map of page-input-name (String) to page-input-value (Object).
+     */
+    public Map getPageInputMap() {
+        return _pageInputs;
+    }
+
+    private CachedFacesBackingInfo getCachedInfo() {
+        ClassLevelCache cache = ClassLevelCache.getCache(getClass());
+        CachedFacesBackingInfo info = (CachedFacesBackingInfo) cache.getCacheObject(CACHED_INFO_KEY);
+
+        if (info == null) {
+            info = new CachedFacesBackingInfo(getClass());
+            cache.setCacheObject(CACHED_INFO_KEY, info);
+        }
+
+        return info;
+    }
+
+    /**
+     * Callback that is invoked when this backing bean is restored as the page itself is restored through a
+     * {@link org.apache.ti.pageflow.annotations.ti.forward &#64;ti.forward} or
+     * {@link org.apache.ti.pageflow.annotations.ti.simpleAction &#64;ti.simpleAction} with
+     * {@link org.apache.ti.pageflow.annotations.ti.forward#navigateTo() navigateTo}={@link org.apache.ti.pageflow.annotations.ti.NavigateTo#currentPage ti.NavigateTo.currentPage}
+     * or
+     * {@link org.apache.ti.pageflow.annotations.ti.forward#navigateTo() navigateTo}={@link org.apache.ti.pageflow.annotations.ti.NavigateTo#currentPage ti.NavigateTo.previousPage}.
+     */
+    protected void onRestore() {
+    }
+
+    /**
+     * Restore this bean (set it as the current one from some dormant state).  This is a framework-invoked method that
+     * should not normally be called directly.
+     */
+    public void restore() {
+        reinitialize();
+        StorageHandler sh = Handlers.get().getStorageHandler();
+        String attrName = InternalUtils.getScopedAttrName(InternalConstants.FACES_BACKING_ATTR);
+        sh.setAttribute(attrName, this);
+        Map newActionOutputs = InternalUtils.getActionOutputMap(false);
+        if (newActionOutputs != null) _pageInputs = newActionOutputs;
+        onRestore();
+    }
+}

Added: struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/FacesBackingBeanFactory.java
URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/FacesBackingBeanFactory.java?rev=240168&view=auto
==============================================================================
--- struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/FacesBackingBeanFactory.java (added)
+++ struts/sandbox/trunk/ti/core/src/java/org/apache/ti/pageflow/FacesBackingBeanFactory.java Thu Aug 25 22:46:03 2005
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+package org.apache.ti.pageflow;
+
+import org.apache.ti.core.factory.Factory;
+import org.apache.ti.core.factory.FactoryUtils;
+import org.apache.ti.pageflow.handler.Handlers;
+import org.apache.ti.pageflow.internal.AnnotationReader;
+import org.apache.ti.pageflow.internal.InternalConstants;
+import org.apache.ti.pageflow.internal.InternalUtils;
+import org.apache.ti.pageflow.xwork.PageFlowActionContext;
+import org.apache.ti.schema.config.PageflowFactories;
+import org.apache.ti.schema.config.PageflowFactory;
+import org.apache.ti.util.config.ConfigUtil;
+import org.apache.ti.util.internal.FileUtils;
+import org.apache.ti.util.logging.Logger;
+
+import java.util.Map;
+
+
+/**
+ * Factory for creating "backing beans" for JavaServer Faces pages.
+ */
+public class FacesBackingBeanFactory
+        extends Factory
+        implements InternalConstants {
+
+    private static final Logger _log = Logger.getInstance(FacesBackingBeanFactory.class);
+
+    private static final String CONTEXT_ATTR = InternalConstants.ATTR_PREFIX + "jsfBackingFactory";
+
+    protected void onCreate() {
+    }
+
+    protected FacesBackingBeanFactory() {
+    }
+
+    public static void init(Map appScope) {
+        PageflowFactories factoriesBean = ConfigUtil.getConfig().getPageflowFactories();
+        FacesBackingBeanFactory factory = null;
+
+        if (factoriesBean != null) {
+            PageflowFactory fcFactoryBean = factoriesBean.getFacesBackingBeanFactory();
+            factory = (FacesBackingBeanFactory) FactoryUtils.getFactory(fcFactoryBean, FacesBackingBeanFactory.class);
+        }
+
+        if (factory == null) factory = new FacesBackingBeanFactory();
+        factory.reinit();
+
+        appScope.put(CONTEXT_ATTR, factory);
+    }
+
+    /**
+     * Called to reinitialize this instance, most importantly after it has been serialized/deserialized.
+     */
+    protected void reinit() {
+        super.reinit();
+    }
+
+    /**
+     * Get a FacesBackingBeanFactory.
+     *
+     * @return a FacesBackingBeanFactory for the given application.  It may or may not be a cached instance.
+     */
+    public static FacesBackingBeanFactory get() {
+        Map appScope = PageFlowActionContext.get().getApplication();
+        FacesBackingBeanFactory factory = (FacesBackingBeanFactory) appScope.get(CONTEXT_ATTR);
+        assert factory != null
+                : FacesBackingBeanFactory.class.getName() + " was not found in application attribute " + CONTEXT_ATTR;
+        factory.reinit();
+        return factory;
+    }
+
+    /**
+     * Get the "backing bean" associated with the JavaServer Faces page for a request.
+     */
+    public FacesBackingBean getFacesBackingBeanForRequest() {
+        String uri = PageFlowActionContext.get().getRequestPath();
+        assert uri.charAt(0) == '/' : uri;
+        String backingClassName = FileUtils.stripFileExtension(uri.substring(1).replace('/', '.'));
+        FacesBackingBean currentBean = InternalUtils.getFacesBackingBean();
+        
+        //
+        // If there is no current backing bean, or if the current one doesn't match the desired classname, create one.
+        //
+        if (currentBean == null || !currentBean.getClass().getName().equals(backingClassName)) {
+            FacesBackingBean bean = null;
+
+            if (FileUtils.uriEndsWith(uri, FACES_EXTENSION) || FileUtils.uriEndsWith(uri, JSF_EXTENSION)) {
+                bean = loadFacesBackingBean(backingClassName);
+                
+                //
+                // If we didn't create (or failed to create) a backing bean, and if this is a JSF request, then create
+                // a default one.  This ensures that there will be a place for things like page inputs, that get stored
+                // in the backing bean across postbacks to the same JSF.
+                //
+                if (bean == null) bean = new DefaultFacesBackingBean();
+                
+                //
+                // If we created a backing bean, invoke its create callback, and tell it to store itself in the session.
+                //
+                if (bean != null) {
+                    try {
+                        bean.create();
+                    } catch (Exception e) {
+                        _log.error("Error while creating backing bean instance of " + backingClassName, e);
+                    }
+
+                    bean.persistInSession();
+                    return bean;
+                }
+            }
+            
+            //
+            // We didn't create a backing bean.  If there's one in the session (an inappropriate one), remove it.
+            //
+            InternalUtils.removeCurrentFacesBackingBean();
+        } else if (currentBean != null) {
+            if (_log.isDebugEnabled()) {
+                _log.debug("Using existing backing bean instance " + currentBean + " for request " +
+                        PageFlowActionContext.get().getRequestPath());
+            }
+
+            currentBean.reinitialize();
+        }
+
+        return currentBean;
+    }
+
+    /**
+     * Load a "backing bean" associated with the JavaServer Faces page for a request.
+     *
+     * @param backingClassName the name of the backing bean class.
+     * @return an initialized FacesBackingBean, or <code>null</code> if an error occurred.
+     */
+    protected FacesBackingBean loadFacesBackingBean(String backingClassName) {
+        try {
+            Class backingClass = null;
+
+            try {
+                backingClass = getFacesBackingBeanClass(backingClassName);
+            } catch (ClassNotFoundException e) {
+                // ignore -- we deal with this and log this immediately below.  getFacesBackingBeanClass() by default
+                // does not throw this exception, but a derived version might.
+            }
+
+            if (backingClass == null) {
+                if (_log.isTraceEnabled()) {
+                    _log.trace("No backing bean class " + backingClassName + " found for request "
+                            + PageFlowActionContext.get().getRequestPath());
+                }
+            } else {
+                AnnotationReader annReader = Handlers.get().getAnnotationHandler().getAnnotationReader(backingClass);
+
+                if (annReader.getJpfAnnotation(backingClass, "facesBacking") != null) {
+                    if (_log.isDebugEnabled()) {
+                        _log.debug("Found backing class " + backingClassName + " for request "
+                                + PageFlowActionContext.get().getRequestPath()
+                                + "; creating a new instance.");
+                    }
+
+                    return getFacesBackingBeanInstance(backingClass);
+                } else {
+                    if (_log.isDebugEnabled()) {
+                        _log.debug("Found matching backing class " + backingClassName + " for request "
+                                + PageFlowActionContext.get().getRequestPath()
+                                + ", but it does not have the " + ANNOTATION_QUALIFIER
+                                + "facesBacking annotation.");
+                    }
+                }
+            }
+        } catch (InstantiationException e) {
+            _log.error("Could not create backing bean instance of " + backingClassName, e);
+        } catch (IllegalAccessException e) {
+            _log.error("Could not create backing bean instance of " + backingClassName, e);
+        }
+
+        return null;
+    }
+
+    private static class DefaultFacesBackingBean
+            extends FacesBackingBean {
+
+    }
+
+    /**
+     * Get a FacesBackingBean class.  By default, this loads the class using the thread context class loader.
+     *
+     * @param className the name of the {@link FacesBackingBean} class to load.
+     * @return the loaded {@link FacesBackingBean} class.
+     * @throws ClassNotFoundException if the requested class could not be found.
+     */
+    public Class getFacesBackingBeanClass(String className)
+            throws ClassNotFoundException {
+        return Handlers.get().getReloadableClassHandler().loadCachedClass(className);
+    }
+
+    /**
+     * Get a FacesBackingBean instance, given a FacesBackingBean class.
+     *
+     * @param beanClass the Class, which must be assignable to {@link FacesBackingBean}.
+     * @return a new FacesBackingBean instance.
+     */
+    public FacesBackingBean getFacesBackingBeanInstance(Class beanClass)
+            throws InstantiationException, IllegalAccessException {
+        assert FacesBackingBean.class.isAssignableFrom(beanClass)
+                : "Class " + beanClass.getName() + " does not extend " + FacesBackingBean.class.getName();
+        return (FacesBackingBean) beanClass.newInstance();
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org