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 [EMAIL PROTECTED] Mappings} instance this [EMAIL 
PROTECTED] Mapping} instance
+     * is associated with.</p>
+     */
+    public Mappings getMappings();
+
+
+    /**
+     * <p>Set the [EMAIL PROTECTED] Mappings} instance this [EMAIL PROTECTED] 
Mapping} instance
+     * is associated with.</p>
+     *
+     * @param mappings The new [EMAIL PROTECTED] 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 [EMAIL PROTECTED] Mapping}, if any, for the specified 
matching
      * <code>pattern</code>.  If there is no such [EMAIL PROTECTED] 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 [EMAIL PROTECTED] 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
 
 
+    /** [EMAIL PROTECTED] */
+    public Mappings getMappings() {
+        return this.mappings;
+    }
+
+
+    /** [EMAIL PROTECTED] */
+    public void setMappings(Mappings mappings) {
+        this.mappings = mappings;
+    }
+
 
     /** [EMAIL PROTECTED] */
     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 [EMAIL PROTECTED] 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 @@
 
 
     /** [EMAIL PROTECTED] */
+    public String getExtension() {
+        return this.extension;
+    }
+
+
+    /** [EMAIL PROTECTED] */
     public Mapping getMapping(String pattern) {
         if (pattern == null) {
             throw new NullPointerException();
@@ -79,6 +99,12 @@
 
 
     /** [EMAIL PROTECTED] */
+    public String[] getPatterns() {
+        return this.patterns;
+    }
+
+
+    /** [EMAIL PROTECTED] */
     public void removeMapping(Mapping mapping) {
         if (mapping == null) {
             throw new NullPointerException();
@@ -86,6 +112,18 @@
         synchronized (mappings) {
             mappings.remove(mapping);
         }
+    }
+
+
+    /** [EMAIL PROTECTED] */
+    public void setExtension(String extension) {
+        this.extension = extension;
+    }
+
+
+    /** [EMAIL PROTECTED] */
+    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
- * [EMAIL PROTECTED] org.apache.shale.faces.ShaleViewHandler ShaleViewHandler}.
+ * [EMAIL PROTECTED] 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: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to