2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

Coverage Report - org.apache.shale.remoting.impl.MethodBindingProcessor
 
Classes in this File Line Coverage Branch Coverage Complexity
MethodBindingProcessor
100%
32/32
N/A
2.833
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to you under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 package org.apache.shale.remoting.impl;
 19  
 
 20  
 import java.io.IOException;
 21  
 import javax.faces.context.FacesContext;
 22  
 import javax.faces.el.MethodBinding;
 23  
 import javax.servlet.ServletContext;
 24  
 import javax.servlet.http.HttpServletResponse;
 25  
 import org.apache.commons.logging.Log;
 26  
 import org.apache.commons.logging.LogFactory;
 27  
 import org.apache.shale.remoting.Constants;
 28  
 import org.apache.shale.remoting.impl.FilteringProcessor;
 29  
 import org.apache.shale.remoting.Processor;
 30  
 
 31  
 /**
 32  
  * <p>Implementation of {@link Processor} which maps a resource identifier
 33  
  * to a method binding expression, then delegates the creation of the current
 34  
  * response to the execution of that method.  The precise details of how a
 35  
  * resource identifier gets mapped are encapsulated in the <code>mapResourceId</code>
 36  
  * method, which may be specialized as desired in a subclass.</p>
 37  
  */
 38  15
 public class MethodBindingProcessor extends FilteringProcessor {
 39  
 
 40  
 
 41  
     // ------------------------------------------------------------ Constructors
 42  
 
 43  
 
 44  
 
 45  
     // ------------------------------------------------------ Instance Variables
 46  
 
 47  
 
 48  
     /**
 49  
      * <p><code>Log</code> instance for this class.</p>
 50  
      */
 51  15
     private transient Log log = null;
 52  
 
 53  
 
 54  
     // -------------------------------------------------------------- Properties
 55  
 
 56  
 
 57  
     /**
 58  
      * <p>Force our default excludes list to be included.</p>
 59  
      *
 60  
      * @param excludes Application specified excludes list
 61  
      */
 62  
     public void setExcludes(String excludes) {
 63  
 
 64  17
         if ((excludes != null) && (excludes.length() > 0)) {
 65  17
             super.setExcludes(Constants.DYNAMIC_RESOURCES_EXCLUDES_DEFAULT
 66  
                               + "," + excludes);
 67  17
         } else {
 68  
             super.setExcludes(Constants.DYNAMIC_RESOURCES_EXCLUDES_DEFAULT);
 69  
         }
 70  
 
 71  17
     }
 72  
 
 73  
 
 74  
     // ------------------------------------------------------- Processor Methods
 75  
 
 76  
 
 77  
     /**
 78  
      * <p>Convert the specified resource identifier into a method binding
 79  
      * expression, and delegate creation of the response to a call to the
 80  
      * identified method.  Call <code>FacesContext.responseComplete()</code>
 81  
      * to tell JavaServer Faces that the entire response has already been
 82  
      * created.</p>
 83  
      *
 84  
      * @param context <code>FacesContext</code> for the current request
 85  
      * @param resourceId Resource identifier used to select the appropriate response
 86  
      *  (this will generally be a context relative path starting with "/")
 87  
      *
 88  
      * @exception IllegalArgumentException if the view identifier is not
 89  
      *  well formed (starting with a '/' character)
 90  
      * @exception IOException if an input/output error occurs
 91  
      * @exception NullPointerException if <code>viewId</code> is <code>null</code>
 92  
      */
 93  
     public void process(FacesContext context, String resourceId) throws IOException {
 94  
 
 95  
         // If someone else has completed the response, we do not have
 96  
         // anything to do
 97  17
         if (context.getResponseComplete()) {
 98  7
             return;
 99  
         }
 100  
 
 101  
         // Filter based on our includes and excludes patterns
 102  10
         if (!accept(resourceId)) {
 103  2
             if (log().isTraceEnabled()) {
 104  
                 log().trace("Resource id '" + resourceId
 105  
                             + "' rejected by include/exclude rules");
 106  
             }
 107  
             // Send an HTTP "not found" response to avoid giving the client
 108  
             // any information about a resource that exists and was refused,
 109  
             // versus a resource that does not exist
 110  2
             sendNotFound(context, resourceId);
 111  2
             context.responseComplete();
 112  2
             return;
 113  
         }
 114  
 
 115  
         // Create and execute a method binding based on this resource identifier
 116  8
         MethodBinding mb = mapResourceId(context, resourceId);
 117  8
         if (log().isDebugEnabled()) {
 118  
             log().debug("Translated resource id '" + resourceId
 119  
                         + "' to method binding expression '"
 120  
                         + mb.getExpressionString() + "'");
 121  
         }
 122  8
         mb.invoke(context, new Object[] { });
 123  
 
 124  
         // Tell JavaServer Faces that the current response has been completed
 125  6
         context.responseComplete();
 126  
 
 127  6
     }
 128  
 
 129  
 
 130  
     // ------------------------------------------------------- Protected Methods
 131  
 
 132  
 
 133  
     /**
 134  
      * <p>Map the specified resource identifier into a corresponding
 135  
      * <code>MethodBinding</code> which identifies the method which will be
 136  
      * called to produce this response.</p>
 137  
      *
 138  
      * <p>The default algorithm performs this conversion as follows:</p>
 139  
      * <ul>
 140  
      * <li>Strip any leading slash character.</li>
 141  
      * <li>Convert embedded slash characters to periods.</li>
 142  
      * <li>Surround the result with "#{" and "}" delimiters.</li>
 143  
      * <li>Ask JavaServer Faces to create a method binding, using this
 144  
      *     expression, for a method that takes no parameters.</li>
 145  
      * </ul>
 146  
      *
 147  
      * @param context <code>FacesContext</code> for the current request
 148  
      * @param resourceId Resource identifier to be mapped
 149  
      */
 150  
     protected MethodBinding mapResourceId(FacesContext context, String resourceId) {
 151  
 
 152  
         // Strip any leading slash character
 153  12
         if (resourceId.startsWith("/")) {
 154  12
             resourceId = resourceId.substring(1);
 155  
         }
 156  
 
 157  
         // Convert nested slash characters into periods
 158  
         // FIXME - this will have problematic results on managed bean names that have periods
 159  12
         resourceId = resourceId.replace('/', '.');
 160  
 
 161  
         // Surround the result with "#{" and "}" delimiters
 162  12
         resourceId = "#{" + resourceId + "}";
 163  
 
 164  
         // Return a MethodBinding expression based on this mapping
 165  12
         return context.getApplication().createMethodBinding(resourceId,
 166  
                 new Class[] { });
 167  
 
 168  
     }
 169  
 
 170  
 
 171  
 
 172  
     // --------------------------------------------------------- Private Methods
 173  
 
 174  
 
 175  
     /**
 176  
      * <p>Return the <code>Log</code> instance to use, creating one if needed.</p>
 177  
      */
 178  
     private Log log() {
 179  
 
 180  10
         if (this.log == null) {
 181  11
             log = LogFactory.getLog(MethodBindingProcessor.class);
 182  
         }
 183  10
         return log;
 184  
 
 185  
     }
 186  
 
 187  
 
 188  
     /**
 189  
      * <p>Send a "not found" HTTP response, if possible.  Otherwise, throw an
 190  
      * <code>IllegalArgumentException</code> that will ripple out.</p>
 191  
      *
 192  
      * @param context <code>FacesContext</code> for the current request
 193  
      * @param resourceId Resource identifier of the resource that was not found
 194  
      *
 195  
      * @exception IllegalArgumentException if we cannot send an HTTP response
 196  
      * @exception IOException if an input/output error occurs
 197  
      *
 198  
      * @since 1.0.4
 199  
      */
 200  
     private void sendNotFound(FacesContext context, String resourceId) throws IOException {
 201  
 
 202  2
         if (servletRequest(context)) {
 203  2
             HttpServletResponse response = (HttpServletResponse)
 204  
               context.getExternalContext().getResponse();
 205  2
             response.sendError(HttpServletResponse.SC_NOT_FOUND, resourceId);
 206  2
         } else {
 207  
             throw new IllegalArgumentException(resourceId);
 208  
         }
 209  
 
 210  2
     }
 211  
 
 212  
 
 213  
     /**
 214  
      * <p>Return <code>true</code> if we are processing a servlet request (as
 215  
      * opposed to a portlet request).</p>
 216  
      *
 217  
      * @param context <code>FacesContext</code> for the current request
 218  
      *
 219  
      * @since 1.0.4
 220  
      */
 221  
     private boolean servletRequest(FacesContext context) {
 222  
 
 223  2
         return context.getExternalContext().getContext() instanceof ServletContext;
 224  
 
 225  
     }
 226  
 
 227  
 
 228  
 }