You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by cr...@apache.org on 2006/01/09 05:53:48 UTC

svn commit: r367198 - in /struts/shale/trunk/core-library/src: java/org/apache/shale/remoting/ java/org/apache/shale/remoting/faces/ java/org/apache/shale/remoting/impl/ java/org/apache/shale/view/ test/org/apache/shale/remoting/impl/

Author: craigmcc
Date: Sun Jan  8 20:53:40 2006
New Revision: 367198

URL: http://svn.apache.org/viewcvs?rev=367198&view=rev
Log:
[38190] Make remoting support for mapping  to a method binding work with the
JSF 1.1 RI, as well as with MyFaces.  It turns out there is a bug in the
Restore View phase functionality in the RI ... mapping the view identifier
to the specified or default suffix is not performed.  This is worked around
by having the mapResourceId() method of MappingImpl skip any suffix that the
FacesServlet is mapped to to be trimmed.

Also had to work around a separate limitation of the RI's RenderKit impl ...
the createResponseWriter() method would *only* create a response writer for
content type "text/html".  Now, ResponseFactory will create an implementation
of its own for requested content types other than "text/html".

Added:
    struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/BasicResponseWriter.java   (with props)
Modified:
    struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/Mapping.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/Mappings.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/RemotingPhaseListener.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/ResponseFactory.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/AbstractResourceProcessor.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/ChainProcessor.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/ClassResourceProcessor.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MappingImpl.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MappingsImpl.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MethodBindingProcessor.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/WebResourceProcessor.java
    struts/shale/trunk/core-library/src/java/org/apache/shale/view/ViewController.java
    struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MappingImplTestCase.java

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/Mapping.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/Mapping.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/Mapping.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/Mapping.java Sun Jan  8 20:53:40 2006
@@ -25,13 +25,29 @@
 public interface Mapping {
 
 
-     /**
-      * <p>Return a description of the mechanism used to return the response
-      * from this processor.  This value <strong>may</strong> be interpreted,
-      * for example, by a JavaServer Faces component that wishes to calculate
-      * an appropriate URL for a component specific resource that is packaged
-      * in a particular manner.</p>
-      */
+    /**
+     * <p>Return the {@link Mappings} instance this {@link Mapping} instance
+     * is associated with.</p>
+     */
+    public Mappings getMappings();
+
+
+    /**
+     * <p>Set the {@link Mappings} instance this {@link Mapping} instance
+     * is associated with.</p>
+     *
+     * @param mappings The new {@link Mappings} instance
+     */
+    public void setMappings(Mappings mappings);
+
+
+    /**
+     * <p>Return a description of the mechanism used to return the response
+     * from this processor.  This value <strong>may</strong> be interpreted,
+     * for example, by a JavaServer Faces component that wishes to calculate
+     * an appropriate URL for a component specific resource that is packaged
+     * in a particular manner.</p>
+     */
      public Mechanism getMechanism();
 
 

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/Mappings.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/Mappings.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/Mappings.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/Mappings.java Sun Jan  8 20:53:40 2006
@@ -39,6 +39,13 @@
 
 
     /**
+     * <p>Return the extension that will replace the <code>FacesServlet</code>
+     * extension pattern, if <code>FacesServlet</code> is extension mapped.</p>
+     */
+    public String getExtension();
+
+
+    /**
      * <p>Return the {@link Mapping}, if any, for the specified matching
      * <code>pattern</code>.  If there is no such {@link Mapping}, return
      * <code>null</code> instead.</p>
@@ -57,6 +64,15 @@
 
 
     /**
+     * <p>Return a list of URL patterns that this application has mapped to
+     * <code>FacesServlet</code>.  This information is useful to renderers that
+     * wish to dynamically calculate URLs that will be guaranteed to trigger
+     * the JSF request processing lifecycle.</p>
+     */
+    public String[] getPatterns();
+
+
+    /**
      * <p>Remove the specified {@link Mapping} from the set of mappings for which
      * remoting services are supplied, if it is currently included.</p>
      *
@@ -65,6 +81,25 @@
      * @exception NullPointerException if <code>mapping</code> is <code>null</code>
      */
     public void removeMapping(Mapping mapping);
+
+
+    /**
+     * <p>Set the extension that will replace the <code>FacesServlet</code>
+     * extension pattern, if <code>FacesServlet</code> is extension mapped.</p>
+     *
+     * @param extension The new extension
+     */
+    public void setExtension(String extension);
+
+
+    /**
+     * <p>Set a list of URL patterns that this application has mapped to
+     * <code>FacesServlet</code>.  If no patterns are known, this SHOULD
+     * be set to a zero-length array, rather than <code>null</code>.</p>
+     *
+     * @param patterns The new list of patterns
+     */
+    public void setPatterns(String patterns[]);
 
 
 }

Added: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/BasicResponseWriter.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/BasicResponseWriter.java?rev=367198&view=auto
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/BasicResponseWriter.java (added)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/BasicResponseWriter.java Sun Jan  8 20:53:40 2006
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2006 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.
+ */
+
+package org.apache.shale.remoting.faces;
+
+import java.io.IOException;
+import java.io.Writer;
+import javax.faces.component.UIComponent;
+import javax.faces.context.ResponseWriter;
+
+/**
+ * <p>Basic implementation of <code>javax.faces.context.ResponseWriter</code>
+ * for use when a content type other than <code>text/html</code> is desired
+ * (such as <code>text/xml</code>).</p>
+ */
+public class BasicResponseWriter extends ResponseWriter {
+    
+
+    // ------------------------------------------------------------ Constructors
+
+
+    /**
+     * <p>Create a new instance configured with the specified properties.</p>
+     *
+     * @param writer <code>Writer</code> to be wrapped
+     * @param contentType Content type of this response
+     * @param characterEncoding Character encoding of thie response
+     **/
+    public BasicResponseWriter(Writer writer, String contentType, String characterEncoding) {
+        this.writer = writer;
+        this.contentType = contentType;
+        this.characterEncoding = characterEncoding;
+    }
+
+
+    // ------------------------------------------------------ Instance Variables
+
+
+    private String characterEncoding = null;
+    private String contentType = "text/html";
+    private boolean open = false; // Is an element currently open?
+    private Writer writer = null;
+
+
+    // ----------------------------------------------------- Mock Object Methods
+
+
+
+    // -------------------------------------------------- ResponseWriter Methods
+
+
+    public ResponseWriter cloneWithWriter(Writer writer) {
+        return new BasicResponseWriter(writer, contentType, characterEncoding);
+    }
+
+
+    public void endDocument() throws IOException {
+        finish();
+        writer.flush();
+    }
+
+
+    public void endElement(String name) throws IOException {
+        if (open) {
+            writer.write("/");
+            finish();
+        } else {
+            writer.write("</");
+            writer.write(name);
+            writer.write(">");
+        }
+    }
+
+
+    public String getCharacterEncoding() {
+        return this.characterEncoding;
+    }
+
+
+    public String getContentType() {
+        return this.contentType;
+    }
+
+
+    public void flush() throws IOException {
+        finish();
+    }
+
+
+    public void startDocument() throws IOException {
+        ; // Do nothing
+    }
+
+
+    public void startElement(String name, UIComponent component) throws IOException {
+        if (name == null) {
+            throw new NullPointerException();
+        }
+        finish();
+        writer.write('<');
+        writer.write(name);
+        open = true;
+    }
+
+
+    public void writeAttribute(String name, Object value, String property) throws IOException {
+        if ((name == null) || (value == null)) {
+            throw new NullPointerException();
+        }
+        if (!open) {
+            throw new IllegalStateException();
+        }
+        writer.write(" ");
+        writer.write(name);
+        writer.write("=\"");
+        if (value instanceof String) {
+            string((String) value);
+        } else {
+            string(value.toString());
+        }
+        writer.write("\"");
+    }
+
+
+    public void writeComment(Object comment) throws IOException {
+        if (comment == null) {
+            throw new NullPointerException();
+        }
+        finish();
+        writer.write("<!-- ");
+        if (comment instanceof String) {
+            writer.write((String) comment);
+        } else {
+            writer.write(comment.toString());
+        }
+        writer.write(" -->");
+    }
+
+
+    public void writeText(Object text, String property) throws IOException {
+        if (text == null) {
+            throw new NullPointerException();
+        }
+        finish();
+        if (text instanceof String) {
+            string((String) text);
+        } else {
+            string(text.toString());
+        }
+    }
+
+
+    public void writeText(char text[], int off, int len) throws IOException {
+        if (text == null) {
+            throw new NullPointerException();
+        }
+        if ((off < 0) || (off > text.length) || (len < 0) || (len > text.length)) {
+            throw new IndexOutOfBoundsException();
+        }
+        finish();
+        string(text, off, len);
+    }
+
+
+    public void writeURIAttribute(String name, Object value, String property) throws IOException {
+        if ((name == null) || (value == null)) {
+            throw new NullPointerException();
+        }
+        if (!open) {
+            throw new IllegalStateException();
+        }
+        writer.write(" ");
+        writer.write(name);
+        writer.write("=\"");
+        if (value instanceof String) {
+            string((String) value);
+        } else {
+            string(value.toString());
+        }
+        writer.write("\"");
+    }
+
+
+    // ---------------------------------------------------------- Writer Methods
+
+
+    public void close() throws IOException {
+        finish();
+        writer.close();
+    }
+
+
+    public void write(char cbuf[], int off, int len) throws IOException {
+        finish();
+        writer.write(cbuf, off, len);
+    }
+
+
+    // --------------------------------------------------------- Support Methods
+
+
+    /**
+     * <p>Write the specified character, filtering if necessary.</p>
+     *
+     * @param ch Character to be written
+     */
+    private void character(char ch) throws IOException {
+
+        if (ch <= 0xff) {
+            // In single byte characters, replace only the five
+            // characters for which well-known entities exist in XML
+            if (ch == 0x22) {
+                writer.write("&quot;");
+            } else if (ch == 0x26) {
+                writer.write("&amp;");
+            } else if (ch == 0x27) {
+                writer.write("&apos;");
+            } else if (ch == 0x3C) {
+                writer.write("&lt;");
+            } else if (ch == 0X3E) {
+                writer.write("&gt;");
+            } else {
+                writer.write(ch);
+            }
+        } else {
+            if (substitution()) {
+                numeric(writer, ch);
+            } else {
+                writer.write(ch);
+            }
+        }
+
+    }
+
+
+    /**
+     * <p>Close any element that is currently open.</p>
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    private void finish() throws IOException {
+
+        if (open) {
+            writer.write(">");
+            open = false;
+        }
+
+    }
+
+
+    /**
+     * <p>Write a numeric character reference for specified character
+     * to the specfied writer.</p>
+     *
+     * @param writer Writer we are writing to
+     * @param ch Character to be translated and appended
+     */
+    private void numeric(Writer writer, char ch) throws IOException {
+
+        writer.write("&#");
+        writer.write(String.valueOf(ch));
+        writer.write(";");
+
+    }
+
+
+    /**
+     * <p>Write the specified characters (after performing suitable
+     * replacement of characters by corresponding entities).</p>
+     *
+     * @param text Character array containing text to be written
+     * @param off Starting offset (zero relative)
+     * @param len Number of characters to be written
+     */
+    private void string(char text[], int off, int len) throws IOException {
+
+        // Process the specified characters
+        for (int i = off; i < (off + len); i++) {
+            character(text[i]);
+        }
+
+    }
+
+
+    /**
+     * <p>Write the specified string (after performing suitable
+     * replacement of characters by corresponding entities).</p>
+     *
+     * @param s String to be filtered and written
+     */
+    private void string(String s) throws IOException {
+
+        for (int i = 0; i < s.length(); i++) {
+            character(s.charAt(i));
+        }
+
+    }
+
+
+    /**
+     * <p>Return true if entity substitution should be performed on double
+     * byte character values.</p>
+     */
+    private boolean substitution() {
+
+        if ("UTF-8".equals(characterEncoding) || "UTF-16".equals(characterEncoding)) {
+            return false;
+        } else {
+            return true;
+        }
+
+    }
+
+
+}

Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/BasicResponseWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/BasicResponseWriter.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/RemotingPhaseListener.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/RemotingPhaseListener.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/RemotingPhaseListener.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/RemotingPhaseListener.java Sun Jan  8 20:53:40 2006
@@ -21,6 +21,7 @@
 import java.util.List;
 import java.util.ResourceBundle;
 import javax.faces.FacesException;
+import javax.faces.application.ViewHandler;
 import javax.faces.context.FacesContext;
 import javax.faces.event.PhaseEvent;
 import javax.faces.event.PhaseId;
@@ -66,7 +67,7 @@
     /**
      * <p>Log instance for this class.</p>
      */
-    private static Log log = LogFactory.getLog(RemotingPhaseListener.class);
+    private transient Log log = null;
 
 
     // --------------------------------------------------- PhaseListener Methods
@@ -82,8 +83,8 @@
 
         // Acquire a reference to the FacesContext for this request
         FacesContext context = event.getFacesContext();
-        if (log.isTraceEnabled()) {
-            log.trace("Checking view identifier '" + context.getViewRoot().getViewId() + "'");
+        if (log().isInfoEnabled()) { // FIXME - trace
+            log().info("Checking view identifier '" + context.getViewRoot().getViewId() + "'");
         }
 
         // Match this view identifier against our configured patterns
@@ -92,10 +93,10 @@
             Mapping mapping = (Mapping) mappings.next();
             String resourceId = mapping.mapViewId(context);
             if (resourceId != null) {
-                if (log.isDebugEnabled()) {
-                    log.debug("View identifier '" + context.getViewRoot().getViewId() +
-                              "' matched pattern '" + mapping.getPattern() +
-                              "' with resource id '" + resourceId + "'");
+                if (log().isInfoEnabled()) { // FIXME - debug
+                    log().info("View identifier '" + context.getViewRoot().getViewId() +
+                                "' matched pattern '" + mapping.getPattern() +
+                                "' with resource id '" + resourceId + "'");
                 }
                 try {
                     mapping.getProcessor().process(context, resourceId);
@@ -195,9 +196,9 @@
             }
             String pattern = pair.substring(0, colon).trim();
             String processorClass = pair.substring(colon + 1).trim();
-            if (log.isInfoEnabled()) {
-                log.info(bundle.getString("mapping.configure"));
-                log.info(pattern + ":" + processorClass);
+            if (log().isInfoEnabled()) {
+                log().info(bundle.getString("mapping.configure"));
+                log().info(pattern + ":" + processorClass);
             }
             Class processorClazz = null;
             try {
@@ -207,6 +208,7 @@
             }
             try {
                 Mapping mapping = (Mapping) clazz.newInstance();
+                mapping.setMappings(mappings);
                 mapping.setMechanism(mechanism);
                 mapping.setPattern(pattern);
                 mapping.setProcessor((Processor) processorClazz.newInstance());
@@ -216,6 +218,19 @@
             }
         }
 
+        // Calculate and set the replacement extension, to be used
+        // if FacesServlet is extension mapped
+        String extension = context.getExternalContext().
+                getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
+        if (extension == null) {
+            extension = ViewHandler.DEFAULT_SUFFIX;
+        }
+        mappings.setExtension(extension);
+
+        // Calculate and set the URL patterns that FacesServlet is mapped with
+        // FIXME - hard coded to "*.faces" for now
+        mappings.setPatterns(new String[] { "*.faces" });
+
     }
 
 
@@ -240,9 +255,9 @@
         }
         Class clazz = null;
         try {
-            if (log.isInfoEnabled()) {
-                log.info(bundle.getString("mappings.configure"));
-                log.info(mappingsClass);
+            if (log().isInfoEnabled()) {
+                log().info(bundle.getString("mappings.configure"));
+                log().info(mappingsClass);
             }
             clazz = loadClass(mappingsClass);
         } catch (Exception e) {
@@ -311,6 +326,19 @@
             cl = this.getClass().getClassLoader();
         }
         return cl.loadClass(name);
+
+    }
+
+
+    /**
+     * <p>Return the <code>Log</code> instance to use, creating one if needed.</p>
+     */
+    private Log log() {
+
+        if (this.log == null) {
+            log = LogFactory.getLog(RemotingPhaseListener.class);
+        }
+        return log;
 
     }
 

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/ResponseFactory.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/ResponseFactory.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/ResponseFactory.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/faces/ResponseFactory.java Sun Jan  8 20:53:40 2006
@@ -190,8 +190,12 @@
             throw new FacesException(e);
         }
 
-        // Construct a ResponseStream that wraps this stream
-        return renderKit(context).createResponseWriter(writer, contentType, null);
+        // Construct a ResponseWriter that wraps this stream
+        if ("text/html".equals(contentType)) {
+            return renderKit(context).createResponseWriter(writer, contentType, null);
+        } else {
+            return new BasicResponseWriter(writer, contentType, null);
+        }
 
 
     }

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/AbstractResourceProcessor.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/AbstractResourceProcessor.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/AbstractResourceProcessor.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/AbstractResourceProcessor.java Sun Jan  8 20:53:40 2006
@@ -50,7 +50,7 @@
     /**
      * <p>The <code>Log</code> instance for this class.</p>
      */
-    private static Log log = LogFactory.getLog(AbstractResourceProcessor.class);
+    private transient Log log = null;
 
 
     // ------------------------------------------------------- Processor Methods
@@ -82,9 +82,13 @@
         // Acquire a URL to the specified resource, if it exists
         // If not, send an HTTP "not found" response
         URL url = getResourceURL(context, resourceId);
+        if (log().isDebugEnabled()) {
+            log().debug("Translated resource id '" + resourceId + "' to URL '" +
+                        url + "'");
+        }
         if (url == null) {
-            if (log.isTraceEnabled()) {
-                log.trace("Resource '" + resourceId + "' not found, returning 404");
+            if (log().isTraceEnabled()) {
+                log().trace("Resource '" + resourceId + "' not found, returning 404");
             }
             sendNotFound(context, resourceId);
             context.responseComplete();
@@ -97,8 +101,8 @@
         long ifModifiedSince = ifModifiedSince(context);
         if ((ifModifiedSince >= 0) &&
             ((ifModifiedSince+1000L) >= getLastModified())) {
-            if (log.isTraceEnabled()) {
-                log.trace("Resource '" + resourceId + "' not modified, returning 304");
+            if (log().isTraceEnabled()) {
+                log().trace("Resource '" + resourceId + "' not modified, returning 304");
             }
             sendNotModified(context, resourceId);
             context.responseComplete();
@@ -521,6 +525,22 @@
         } else {
             throw new IllegalArgumentException(resourceId);
         }
+
+    }
+
+
+    // --------------------------------------------------------- Private Methods
+
+
+    /**
+     * <p>Return the <code>Log</code> instance to use, creating one if needed.</p>
+     */
+    private Log log() {
+
+        if (this.log == null) {
+            log = LogFactory.getLog(AbstractResourceProcessor.class);
+        }
+        return log;
 
     }
 

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/ChainProcessor.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/ChainProcessor.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/ChainProcessor.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/ChainProcessor.java Sun Jan  8 20:53:40 2006
@@ -50,7 +50,7 @@
     /**
      * <p>The <code>Log</code> instance for this class.</p>
      */
-    private static Log log = LogFactory.getLog(ChainProcessor.class);
+    private transient Log log = null;
 
 
     // ------------------------------------------------------- Processor Methods
@@ -74,13 +74,19 @@
      */
     public void process(FacesContext context, String resourceId) throws IOException {
 
+        if (log().isDebugEnabled()) {
+            log().debug("Translated resource id '" + resourceId + "' to catalog '" +
+                        mapCatalog(context, resourceId) + "' and command '" +
+                        mapCommand(context, resourceId) + "'");
+        }
+
         // Identify the Commons Chain catalog we will be using
         String catalogName = mapCatalog(context, resourceId);
         Catalog catalog = CatalogFactory.getInstance().getCatalog(catalogName);
         if (catalog == null) {
-            if (log.isErrorEnabled()) {
-                log.error("Cannot find catalog '" + catalogName + "' for resource '" +
-                          resourceId + "'");
+            if (log().isErrorEnabled()) {
+                log().error("Cannot find catalog '" + catalogName + "' for resource '" +
+                            resourceId + "'");
             }
             sendNotFound(context, resourceId);
             context.responseComplete();
@@ -91,9 +97,9 @@
         String commandName = mapCommand(context, resourceId);
         Command command = catalog.getCommand(commandName);
         if (command == null) {
-            if (log.isErrorEnabled()) {
-                log.error("Cannot find command '" + commandName + "' in catalog '" +
-                          catalogName + "' for resource '" + resourceId + "'");
+            if (log().isErrorEnabled()) {
+                log().error("Cannot find command '" + commandName + "' in catalog '" +
+                            catalogName + "' for resource '" + resourceId + "'");
             }
             sendNotFound(context, resourceId);
             context.responseComplete();
@@ -104,10 +110,10 @@
         try {
             command.execute(createContext(context, resourceId));
         } catch (Exception e) {
-            if (log.isErrorEnabled()) {
-                log.error("Exception executing command '" + commandName +
-                          "' from catalog '" + catalogName + "' for resource '" +
-                          resourceId + "'", e);
+            if (log().isErrorEnabled()) {
+                log().error("Exception executing command '" + commandName +
+                            "' from catalog '" + catalogName + "' for resource '" +
+                            resourceId + "'", e);
             }
             sendServerError(context, resourceId, e);
         }
@@ -215,7 +221,7 @@
      *
      * @param context <code>FacesContext</code> for the current request
      * @param resourceId Resource identifier of the resource that was not found
-     * @param exception Server exception to be reported
+     * @param e Server exception to be reported
      *
      * @exception FacesException if we cannot send an HTTP response
      * @exception IOException if an input/output error occurs
@@ -243,6 +249,22 @@
     protected boolean servletRequest(FacesContext context) {
 
         return context.getExternalContext().getContext() instanceof ServletContext;
+
+    }
+
+
+    // --------------------------------------------------------- Private Methods
+
+
+    /**
+     * <p>Return the <code>Log</code> instance to use, creating one if needed.</p>
+     */
+    private Log log() {
+
+        if (this.log == null) {
+            log = LogFactory.getLog(ChainProcessor.class);
+        }
+        return log;
 
     }
 

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/ClassResourceProcessor.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/ClassResourceProcessor.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/ClassResourceProcessor.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/ClassResourceProcessor.java Sun Jan  8 20:53:40 2006
@@ -50,7 +50,7 @@
     /**
      * <p>Log instance for this class.</p>
      */
-    private static Log log = LogFactory.getLog(ClassResourceProcessor.class);
+    private transient Log log = null;
 
 
     // -------------------------------------------------------------- Properties
@@ -66,9 +66,9 @@
         // Disallow access to Java classes
         String resourceIdLower = resourceId.toLowerCase();
         if (resourceIdLower.endsWith(".class")) {
-            if (log.isWarnEnabled()) {
-                log.warn(bundle.getString("resource.refuse"));
-                log.warn(resourceId);
+            if (log().isWarnEnabled()) {
+                log().warn(bundle.getString("resource.refuse"));
+                log().warn(resourceId);
             }
             return null;
         }
@@ -79,14 +79,14 @@
         // Return a URL to the class loader resource (if it exists)
         try {
             URL url = cl.getResource(resourceId.substring(1));
-            if (log.isDebugEnabled()) {
-                log.debug("getResource(" + resourceId + ") --> " + url);
+            if (log().isDebugEnabled()) {
+                log().debug("getResource(" + resourceId + ") --> " + url);
             }
             return url;
         } catch (Exception e) {
-            if (log.isErrorEnabled()) {
-                log.error(bundle.getString("resource.exception"), e);
-                log.error(resourceId);
+            if (log().isErrorEnabled()) {
+                log().error(bundle.getString("resource.exception"), e);
+                log().error(resourceId);
             }
             return null;
         }
@@ -97,6 +97,19 @@
 
     // --------------------------------------------------------- Private Methods
 
+
+
+    /**
+     * <p>Return the <code>Log</code> instance to use, creating one if needed.</p>
+     */
+    private Log log() {
+
+        if (this.log == null) {
+            log = LogFactory.getLog(ClassResourceProcessor.class);
+        }
+        return log;
+
+    }
 
 
 }

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MappingImpl.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MappingImpl.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MappingImpl.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MappingImpl.java Sun Jan  8 20:53:40 2006
@@ -19,6 +19,7 @@
 import javax.faces.application.ViewHandler;
 import javax.faces.context.FacesContext;
 import org.apache.shale.remoting.Mapping;
+import org.apache.shale.remoting.Mappings;
 import org.apache.shale.remoting.Mechanism;
 import org.apache.shale.remoting.Processor;
 
@@ -65,6 +66,7 @@
     // ------------------------------------------------------ Instance Variables
 
 
+    private Mappings mappings = null;
     private String match = null; // Non-wildcard part of the pattern
     private Mechanism mechanism = null;
     private String pattern = null;
@@ -75,6 +77,17 @@
     // --------------------------------------------------------- Mapping Methods
 
 
+    /** {@inheritDoc} */
+    public Mappings getMappings() {
+        return this.mappings;
+    }
+
+
+    /** {@inheritDoc} */
+    public void setMappings(Mappings mappings) {
+        this.mappings = mappings;
+    }
+
 
     /** {@inheritDoc} */
     public Mechanism getMechanism() {
@@ -139,12 +152,9 @@
     public String mapViewId(FacesContext context) {
 
         // Extract the view identifier we will be using to match against
-        // If FacesServlet is extension mapped (such as to "*.faces"), we
-        // will want to strip the replaced extension (typically ".jsp") first
-        String viewId = context.getViewRoot().getViewId();
-        String extension = extension(context);
-        if (viewId.endsWith(extension)) {
-            viewId = viewId.substring(0, viewId.length() - extension.length());
+        String viewId = viewId(context);
+        if (viewId == null) {
+            return null;
         }
 
         // Perform prefix or extension matching as requested
@@ -189,27 +199,48 @@
 
 
     /**
-     * <p>The cached extension value (calculated the first time it is requested).</p>
-     */
-    private String extension = null;
-
-
-    /**
-     * <p>Return the extension which will be used to replace the incoming
-     * extension if <code>FacesServlet</code> is extension mapped.  This value
-     * will be cached the first time that it is calculated.</p>
+     * <p>Extract and return the view identifier for this request, after
+     * stripping any replacement suffix if <code>FacesServlet</code> is
+     * being extension mapped.</p>
      *
      * @param context <code>FacesContext</code> for the current request
      */
-    private String extension(FacesContext context) {
+    private String viewId(FacesContext context) {
+
+        // Get the raw view identifier
+        String viewId = context.getViewRoot().getViewId();
 
-        if (extension == null) {
-            extension = context.getExternalContext().getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
-            if (extension == null) {
-                extension = ViewHandler.DEFAULT_SUFFIX;
+        // If the view identifier ends with the configured (or default)
+        // replacement suffix, just strip it and return
+        String extension = mappings.getExtension();
+        if ((extension != null) && (viewId.endsWith(extension))) {
+            return viewId.substring(0, viewId.length() - extension.length());
+        }
+
+        // The JSF RI (version 1.1) has a bug where it does *not* replace
+        // the incoming extension during Restore View phase, as is required
+        // by Section 2.2.1 of the JSF Specification.  As a result, the view
+        // identifier immediately after Restore View completes will be something
+        // like "/index.faces" instead of "/index.jsp".  To work around this
+        // bug, walk through the URL patterns to which FacesServlet is mapped.
+        // If we detect an extension matching pattern that is found on our
+        // current view identifier, strip that and return as well.
+        String patterns[] = mappings.getPatterns();
+        if ((patterns == null) || (patterns.length < 1)) {
+            return viewId;
+        }
+        for (int i = 0; i < patterns.length; i++) {
+            if (!patterns[i].startsWith("*.")) {
+                continue;
+            }
+            String match = patterns[i].substring(1);
+            if (viewId.endsWith(match)) {
+                return viewId.substring(0, viewId.length() - match.length());
             }
         }
-        return extension;
+
+        // No matches, so just return what we have
+        return viewId;
 
     }
 

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MappingsImpl.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MappingsImpl.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MappingsImpl.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MappingsImpl.java Sun Jan  8 20:53:40 2006
@@ -20,6 +20,7 @@
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import javax.faces.application.ViewHandler;
 import org.apache.shale.remoting.Mapping;
 import org.apache.shale.remoting.Mappings;
 
@@ -33,11 +34,24 @@
 
 
     /**
+     * <p>The extension that will replace the <code>FacesServlet</code>
+     * extension, if the servlet is extension mapped.</p>
+     */
+    private String extension = ViewHandler.DEFAULT_SUFFIX;
+
+
+    /**
      * <p>A list of {@link Mapping} instances we understand.</p>
      */
     private List mappings = new ArrayList();
 
 
+    /**
+     * <p>The list of URL patterns for <code>FacesServlet</code>.</p>
+     */
+    private String patterns[] = new String[0];
+
+
     // -------------------------------------------------------- Mappings Methods
 
 
@@ -55,6 +69,12 @@
 
 
     /** {@inheritDoc} */
+    public String getExtension() {
+        return this.extension;
+    }
+
+
+    /** {@inheritDoc} */
     public Mapping getMapping(String pattern) {
         if (pattern == null) {
             throw new NullPointerException();
@@ -79,6 +99,12 @@
 
 
     /** {@inheritDoc} */
+    public String[] getPatterns() {
+        return this.patterns;
+    }
+
+
+    /** {@inheritDoc} */
     public void removeMapping(Mapping mapping) {
         if (mapping == null) {
             throw new NullPointerException();
@@ -86,6 +112,18 @@
         synchronized (mappings) {
             mappings.remove(mapping);
         }
+    }
+
+
+    /** {@inheritDoc} */
+    public void setExtension(String extension) {
+        this.extension = extension;
+    }
+
+
+    /** {@inheritDoc} */
+    public void setPatterns(String patterns[]) {
+        this.patterns = patterns;
     }
 
 

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MethodBindingProcessor.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MethodBindingProcessor.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MethodBindingProcessor.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MethodBindingProcessor.java Sun Jan  8 20:53:40 2006
@@ -19,6 +19,8 @@
 import java.io.IOException;
 import javax.faces.context.FacesContext;
 import javax.faces.el.MethodBinding;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.shale.remoting.Processor;
 
 /**
@@ -35,6 +37,12 @@
 
 
 
+    // ------------------------------------------------------ Instance Variables
+
+
+    private transient Log log = null;
+
+
     // ------------------------------------------------------- Processor Methods
 
 
@@ -58,6 +66,11 @@
 
         // Create and execute a method binding based on this resource identifier
         MethodBinding mb = mapResourceId(context, resourceId);
+        if (log().isInfoEnabled()) {
+            log().info("Translated resource id '" + resourceId +
+                        "' to method binding expression '" +
+                        mb.getExpressionString() + "'");
+        }
         mb.invoke(context, new Object[] { });
 
         // Tell JavaServer Faces that the current response has been completed
@@ -106,6 +119,22 @@
 
     }
 
+
+
+    // --------------------------------------------------------- Private Methods
+
+
+    /**
+     * <p>Return the <code>Log</code> instance to use, creating one if needed.</p>
+     */
+    private Log log() {
+
+        if (this.log == null) {
+            log = LogFactory.getLog(MethodBindingProcessor.class);
+        }
+        return log;
+
+    }
 
 
 }

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/WebResourceProcessor.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/WebResourceProcessor.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/WebResourceProcessor.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/WebResourceProcessor.java Sun Jan  8 20:53:40 2006
@@ -50,7 +50,7 @@
     /**
      * <p>Log instance for this class.</p>
      */
-    private static Log log = LogFactory.getLog(WebResourceProcessor.class);
+    private transient Log log = null;
 
 
     // -------------------------------------------------------------- Properties
@@ -66,18 +66,18 @@
         // Disallow access to resources in reserved directories
         String resourceIdUpper = resourceId.toUpperCase();
         if (resourceIdUpper.startsWith("/WEB-INF") || resourceIdUpper.startsWith("/META-INF")) {
-            if (log.isWarnEnabled()) {
-                log.warn(bundle.getString("resource.refuse"));
-                log.warn(resourceId);
+            if (log().isWarnEnabled()) {
+                log().warn(bundle.getString("resource.refuse"));
+                log().warn(resourceId);
             }
             return null;
         }
 
         // Disallow access to JSP and JSP fragment sources
         if(resourceIdUpper.endsWith(".JSP") || resourceIdUpper.endsWith(".JSPF")) {
-            if (log.isWarnEnabled()) {
-                log.warn(bundle.getString("resource.refuse"));
-                log.warn(resourceId);
+            if (log().isWarnEnabled()) {
+                log().warn(bundle.getString("resource.refuse"));
+                log().warn(resourceId);
             }
             return null;
         }
@@ -94,9 +94,9 @@
             }
             return url;
         } catch (Exception e) {
-            if (log.isErrorEnabled()) {
-                log.error(bundle.getString("resource.exception"), e);
-                log.error(resourceId);
+            if (log().isErrorEnabled()) {
+                log().error(bundle.getString("resource.exception"), e);
+                log().error(resourceId);
             }
             return null;
         }
@@ -107,6 +107,19 @@
 
     // --------------------------------------------------------- Private Methods
 
+
+
+    /**
+     * <p>Return the <code>Log</code> instance to use, creating one if needed.</p>
+     */
+    private Log log() {
+
+        if (this.log == null) {
+            log = LogFactory.getLog(WebResourceProcessor.class);
+        }
+        return log;
+
+    }
 
 
 }

Modified: struts/shale/trunk/core-library/src/java/org/apache/shale/view/ViewController.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/view/ViewController.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/java/org/apache/shale/view/ViewController.java (original)
+++ struts/shale/trunk/core-library/src/java/org/apache/shale/view/ViewController.java Sun Jan  8 20:53:40 2006
@@ -46,7 +46,7 @@
  * <p>
  * To be useful, the ViewController must be plugged into the application
  * lifecycle through a custom JSF ViewHandler, like the
- * {@link org.apache.shale.faces.ShaleViewHandler ShaleViewHandler}.
+ * {@link org.apache.shale.view.faces.ViewViewHandler ViewViewHandler}.
  * </p>
  *
  * <h3>Registering a ViewController backing bean</h3>

Modified: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MappingImplTestCase.java
URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MappingImplTestCase.java?rev=367198&r1=367197&r2=367198&view=diff
==============================================================================
--- struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MappingImplTestCase.java (original)
+++ struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MappingImplTestCase.java Sun Jan  8 20:53:40 2006
@@ -44,7 +44,11 @@
 
         super.setUp();
         facesContext.setViewRoot(new UIViewRoot());
+        mappings = new MappingsImpl();
+        mappings.setExtension(".jsp");
+        mappings.setPatterns(new String[] { "*.faces" });
         mapping = new MappingImpl();
+        mapping.setMappings(mappings);
 
     }
 
@@ -73,6 +77,10 @@
     private MappingImpl mapping = null;
 
 
+    // The parent instance we must configure for testing
+    private MappingsImpl mappings = null;
+
+
     // ------------------------------------------------------------ Test Methods
 
 
@@ -92,6 +100,19 @@
         assertEquals("/bar", mapping.mapViewId(facesContext));
         facesContext.getViewRoot().setViewId("/bar/baz.foo");
         assertEquals("/bar/baz", mapping.mapViewId(facesContext));
+        facesContext.getViewRoot().setViewId("/foo/bar.foo" + mappings.getExtension());
+        assertEquals("/foo/bar", mapping.mapViewId(facesContext));
+
+    }
+
+
+    // Test extension mapping workaround for JSF RI bug that leaves
+    // "*.faces" on the view id after Restore View phase
+    public void testExtensionExtra() {
+
+        mapping.setPattern("*.bop");
+        facesContext.getViewRoot().setViewId("/foo/bar.bop" + mappings.getPatterns()[0].substring(1));
+        assertEquals("/foo/bar", mapping.mapViewId(facesContext));
 
     }
 



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