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

For more information, please explore the Attic.

Coverage Report - org.apache.shale.remoting.faces.MappingsHelper
 
Classes in this File Line Coverage Branch Coverage Complexity
MappingsHelper
100%
101/101
N/A
9.143
 
 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.faces;
 19  
 
 20  
 import java.io.InputStream;
 21  
 import java.lang.reflect.Method;
 22  
 import java.net.URL;
 23  
 import java.util.ArrayList;
 24  
 import java.util.List;
 25  
 import java.util.ResourceBundle;
 26  
 import javax.faces.FacesException;
 27  
 import javax.faces.application.ViewHandler;
 28  
 import javax.faces.context.FacesContext;
 29  
 import javax.xml.parsers.DocumentBuilder;
 30  
 import javax.xml.parsers.DocumentBuilderFactory;
 31  
 import org.apache.commons.logging.Log;
 32  
 import org.apache.commons.logging.LogFactory;
 33  
 import org.apache.shale.remoting.Constants;
 34  
 import org.apache.shale.remoting.Mapping;
 35  
 import org.apache.shale.remoting.Mappings;
 36  
 import org.apache.shale.remoting.Mechanism;
 37  
 import org.apache.shale.remoting.Processor;
 38  
 import org.apache.shale.remoting.impl.FilteringProcessor;
 39  
 import org.apache.shale.remoting.impl.MappingImpl;
 40  
 import org.apache.shale.remoting.impl.MappingsImpl;
 41  
 import org.w3c.dom.Document;
 42  
 import org.w3c.dom.Node;
 43  
 import org.w3c.dom.NodeList;
 44  
 
 45  
 /**
 46  
  * <p>Helper bean for accessing the {@link Mappings} instance for this
 47  
  * application, creating it the first time if necessary.</p>
 48  
  *
 49  
  * @since 1.0.1
 50  
  */
 51  7
 public class MappingsHelper {
 52  
 
 53  
 
 54  
     // ------------------------------------------------------ Instance Variables
 55  
 
 56  
 
 57  
     /**
 58  
      * <p><code>ResourceBundle</code> containing our localized messages.</p>
 59  
      */
 60  7
     private ResourceBundle bundle =
 61  
             ResourceBundle.getBundle("org.apache.shale.remoting.Bundle");
 62  
 
 63  
 
 64  
     /**
 65  
      * <p>Log instance for this class.</p>
 66  
      */
 67  7
     private transient Log log = null;
 68  
 
 69  
 
 70  
     // ---------------------------------------------------------- Public Methods
 71  
 
 72  
 
 73  
     /**
 74  
      * <p>Return the {@link Mappings} instance for this web application,
 75  
      * creating it if necessary.</p>
 76  
      *
 77  
      * @param context <code>FacesContext</code> for the current request
 78  
      */
 79  
     public Mappings getMappings(FacesContext context) {
 80  
 
 81  6
         Mappings mappings = (Mappings)
 82  
             context.getExternalContext().getApplicationMap().
 83  
                 get(Constants.MAPPINGS_ATTR);
 84  6
         if (mappings == null) {
 85  4
             mappings = createMappings(context);
 86  1
             context.getExternalContext().getApplicationMap().
 87  
                     put(Constants.MAPPINGS_ATTR, mappings);
 88  
         }
 89  3
         return mappings;
 90  
 
 91  
     }
 92  
 
 93  
 
 94  
     // --------------------------------------------------------- Private Methods
 95  
 
 96  
 
 97  
     /**
 98  
      * <p>Configure {@link Mapping} instances on the specified {@link Mappings}
 99  
      * instance, for the specified context initialization parameter.</p>
 100  
      *
 101  
      * @param context <code>FacesContext</code> for the current request
 102  
      * @param mappings {@link Mappings} instance being configured
 103  
      * @param paramName Context initialization parameter name to process
 104  
      * @param excludesName Context initialization parameter containing our
 105  
      *  exclude patterns
 106  
      * @param excludesDefault Default exclude patterns if none are configured
 107  
      * @param includesName Context initialization parameter containing our
 108  
      *  include patterns
 109  
      * @param includesDefault Default include patterns if none are configured
 110  
      * @param mechanism {@link Mechanism} to configure on created instances
 111  
      * @param defaultValue Default value (if any) if not specified
 112  
      *
 113  
      * @exception FacesException if a new Mapping instance cannot be created
 114  
      *  or configured
 115  
      */
 116  
     private void configureMappings(FacesContext context, Mappings mappings,
 117  
                                    String paramName,
 118  
                                    String excludesName, String excludesDefault,
 119  
                                    String includesName, String includesDefault,
 120  
                                    Mechanism mechanism, String defaultValue) {
 121  
 
 122  
         // Identify the Mapping implementation class to be used
 123  15
         Class clazz = MappingImpl.class;
 124  11
         String mappingClass =
 125  
           context.getExternalContext().getInitParameter(Constants.MAPPING_CLASS);
 126  11
         if (mappingClass != null) {
 127  
             try {
 128  
                 clazz = loadClass(mappingClass);
 129  
             } catch (Exception e) {
 130  
                 throw new FacesException(e);
 131  
             }
 132  
         }
 133  
 
 134  
         // Acquire the context initialization parameter value (or default it)
 135  11
         String paramValue = context.getExternalContext().getInitParameter(paramName);
 136  11
         if (paramValue == null) {
 137  10
             paramValue = defaultValue;
 138  
         }
 139  11
         if (paramValue == null) {
 140  2
             return;
 141  
         }
 142  
 
 143  
         // Configure new Mapping instances for each specified pattern:classname pair
 144  
         while (true) {
 145  15
             paramValue = paramValue.trim();
 146  15
             if (paramValue.length() == 0) {
 147  6
                 break;
 148  
             }
 149  9
             String pair = null;
 150  9
             int comma = paramValue.indexOf(',');
 151  9
             if (comma >= 0) {
 152  
                 pair = paramValue.substring(0, comma).trim();
 153  
                 paramValue = paramValue.substring(comma + 1);
 154  
             } else {
 155  9
                 pair = paramValue.trim();
 156  9
                 paramValue = "";
 157  
             }
 158  9
             int colon = pair.indexOf(':');
 159  9
             if (colon < 0) {
 160  
                 throw new IllegalArgumentException(pair);
 161  
             }
 162  9
             String pattern = pair.substring(0, colon).trim();
 163  9
             String processorClass = pair.substring(colon + 1).trim();
 164  9
             if (log().isInfoEnabled()) {
 165  9
                 log().info(bundle.getString("mapping.configure"));
 166  9
                 log().info(pattern + ":" + processorClass);
 167  
             }
 168  9
             Class processorClazz = null;
 169  
             try {
 170  9
                 processorClazz = loadClass(processorClass);
 171  
             } catch (Exception e) {
 172  
                 throw new FacesException(e);
 173  9
             }
 174  
             try {
 175  9
                 Mapping mapping = (Mapping) clazz.newInstance();
 176  9
                 mapping.setMappings(mappings);
 177  9
                 mapping.setMechanism(mechanism);
 178  9
                 mapping.setPattern(pattern);
 179  8
                 Processor processor = (Processor) processorClazz.newInstance();
 180  8
                 if (processor instanceof FilteringProcessor) {
 181  8
                     String excludesPatterns =
 182  
                       context.getExternalContext().getInitParameter(excludesName);
 183  8
                     if (excludesPatterns == null) {
 184  7
                         excludesPatterns = excludesDefault;
 185  
                     }
 186  8
                     ((FilteringProcessor) processor).setExcludes(excludesPatterns);
 187  7
                     String includesPatterns =
 188  
                       context.getExternalContext().getInitParameter(includesName);
 189  7
                     if (includesPatterns == null) {
 190  6
                         includesPatterns = includesDefault;
 191  
                     }
 192  7
                     ((FilteringProcessor) processor).setIncludes(includesPatterns);
 193  
                 }
 194  6
                 mapping.setProcessor(processor);
 195  6
                 mappings.addMapping(mapping);
 196  3
             } catch (RuntimeException e) {
 197  3
                 throw e;
 198  
             } catch (Exception e) {
 199  
                 throw new FacesException(e);
 200  6
             }
 201  6
         }
 202  
 
 203  6
     }
 204  
 
 205  
 
 206  
     /**
 207  
      * <p>Create and configure a {@link Mappings} instance based on the relevant
 208  
      * context initialization parameters for this web application.</p>
 209  
      *
 210  
      * @param context <code>FacesContext</code> for the current request
 211  
      *
 212  
      * @exception FacesException if a new Mappings instance cannot be created
 213  
      *  or configured
 214  
      */
 215  
     private Mappings createMappings(FacesContext context) {
 216  
 
 217  
         // Instantiate a Mappings instance to configure
 218  4
         Mappings mappings = null;
 219  4
         String mappingsClass = MappingsImpl.class.getName();
 220  4
         String mappingsClassParam =
 221  
           context.getExternalContext().getInitParameter(Constants.MAPPINGS_CLASS);
 222  4
         if (mappingsClassParam != null) {
 223  
             mappingsClass = mappingsClassParam;
 224  
         }
 225  4
         Class clazz = null;
 226  
         try {
 227  4
             if (log().isInfoEnabled()) {
 228  4
                 log().info(bundle.getString("mappings.configure"));
 229  4
                 log().info(mappingsClass);
 230  
             }
 231  4
             clazz = loadClass(mappingsClass);
 232  
         } catch (Exception e) {
 233  
             throw new FacesException(e);
 234  4
         }
 235  
         try {
 236  4
             mappings = (Mappings) clazz.newInstance();
 237  
         } catch (Exception e) {
 238  
             throw new FacesException(e);
 239  4
         }
 240  
 
 241  
         // Configure the Mapping instances for this Mappings instance
 242  4
         configureMappings(context, mappings, Constants.CLASS_RESOURCES_PARAM,
 243  
                           Constants.CLASS_RESOURCES_EXCLUDES,
 244  
                           Constants.CLASS_RESOURCES_EXCLUDES_DEFAULT,
 245  
                           Constants.CLASS_RESOURCES_INCLUDES,
 246  
                           Constants.CLASS_RESOURCES_INCLUDES_DEFAULT,
 247  
                           Mechanism.CLASS_RESOURCE,
 248  
                           "/static/*:org.apache.shale.remoting.impl.ClassResourceProcessor");
 249  3
         configureMappings(context, mappings, Constants.DYNAMIC_RESOURCES_PARAM,
 250  
                           Constants.DYNAMIC_RESOURCES_EXCLUDES,
 251  
                           Constants.DYNAMIC_RESOURCES_EXCLUDES_DEFAULT,
 252  
                           Constants.DYNAMIC_RESOURCES_INCLUDES,
 253  
                           Constants.DYNAMIC_RESOURCES_INCLUDES_DEFAULT,
 254  
                           Mechanism.DYNAMIC_RESOURCE,
 255  
                           "/dynamic/*:org.apache.shale.remoting.impl.MethodBindingProcessor");
 256  2
         configureMappings(context, mappings, Constants.OTHER_RESOURCES_PARAM,
 257  
                           Constants.OTHER_RESOURCES_EXCLUDES,
 258  
                           Constants.OTHER_RESOURCES_EXCLUDES_DEFAULT,
 259  
                           Constants.OTHER_RESOURCES_INCLUDES,
 260  
                           Constants.OTHER_RESOURCES_INCLUDES_DEFAULT,
 261  
                           Mechanism.OTHER_RESOURCE,
 262  
                           null);
 263  2
         configureMappings(context, mappings, Constants.WEBAPP_RESOURCES_PARAM,
 264  
                           Constants.WEBAPP_RESOURCES_EXCLUDES,
 265  
                           Constants.WEBAPP_RESOURCES_EXCLUDES_DEFAULT,
 266  
                           Constants.WEBAPP_RESOURCES_INCLUDES,
 267  
                           Constants.WEBAPP_RESOURCES_INCLUDES_DEFAULT,
 268  
                           Mechanism.WEBAPP_RESOURCE,
 269  
                           "/webapp/*:org.apache.shale.remoting.impl.WebResourceProcessor");
 270  
 
 271  
         // Calculate and set the replacement extension, to be used
 272  
         // if FacesServlet is extension mapped
 273  1
         String extension = context.getExternalContext().
 274  
                 getInitParameter(ViewHandler.DEFAULT_SUFFIX_PARAM_NAME);
 275  1
         if (extension == null) {
 276  1
             extension = ViewHandler.DEFAULT_SUFFIX;
 277  
         }
 278  1
         mappings.setExtension(extension);
 279  
 
 280  
         // Calculate and set the URL patterns that FacesServlet is mapped with
 281  
         // FIXME - hard coded to "*.faces" for now
 282  1
         String[] patterns = patterns(context);
 283  1
         if (log().isTraceEnabled()) {
 284  
             for (int i = 0; i < patterns.length; i++) {
 285  
                 log().trace("FacesServlet is mapped with URL pattern '" + patterns[i] + "'");
 286  
             }
 287  
         }
 288  1
         mappings.setPatterns(patterns);
 289  
 
 290  
         // Calculate the index of the pattern to use by default
 291  1
         int patternIndex = 0;
 292  1
         String patternIndexString =
 293  
           context.getExternalContext().getInitParameter(Constants.FACES_SERVLET_URL_PARAM);
 294  1
         if (patternIndexString != null) {
 295  
             patternIndex = Integer.parseInt(patternIndexString.trim());
 296  
         }
 297  1
         if (patternIndex >= patterns.length) {
 298  1
             log.warn("FacesServlet pattern index of " + patternIndex
 299  
                      + " does not match any specified pattern");
 300  
         }
 301  1
         mappings.setPatternIndex(patternIndex);
 302  
 
 303  
         // Return the configured Mappings instance
 304  1
         return mappings;
 305  
 
 306  
     }
 307  
 
 308  
 
 309  
     /**
 310  
      * <p>Load the specified class from the web application class loader
 311  
      * (if possible).</p>
 312  
      *
 313  
      * @param name Fully qualified class name
 314  
      *
 315  
      * @exception ClassNotFoundException if the specified class cannot
 316  
      *  be loaded
 317  
      */
 318  
     private Class loadClass(String name) throws ClassNotFoundException {
 319  
 
 320  13
         ClassLoader cl = Thread.currentThread().getContextClassLoader();
 321  13
         if (cl == null) {
 322  
             cl = this.getClass().getClassLoader();
 323  
         }
 324  13
         return cl.loadClass(name);
 325  
 
 326  
     }
 327  
 
 328  
 
 329  
     /**
 330  
      * <p>Return the <code>Log</code> instance to use, creating one if needed.</p>
 331  
      */
 332  
     private Log log() {
 333  
 
 334  41
         if (this.log == null) {
 335  4
             log = LogFactory.getLog(MappingsHelper.class);
 336  
         }
 337  41
         return log;
 338  
 
 339  
     }
 340  
 
 341  
 
 342  
     /**
 343  
      * <p>Return an array of URL patterns that <code>FacesServlet</code> is
 344  
      * mapped to for this application.</p>
 345  
      *
 346  
      * @param context <code>FacesContext</code> for the current request
 347  
      */
 348  
     private String[] patterns(FacesContext context) {
 349  
 
 350  1
         Document document = null;
 351  1
         InputStream stream = null;
 352  
 
 353  
         try {
 354  
 
 355  
             // Acquire a URL for /WEB-INF/web.xml (if any)
 356  1
             Object ctxt = context.getExternalContext().getContext();
 357  1
             Method method =
 358  
                     ctxt.getClass().getMethod("getResource",
 359  
                                               new Class[] { String.class });
 360  1
             URL url = (URL) method.invoke(ctxt, new Object[] { "/WEB-INF/web.xml" });
 361  1
             if (url == null) {
 362  1
                 if (log().isTraceEnabled()) {
 363  
                     log().trace("No /WEB-INF/web.xml resource available, returning empty list");
 364  
                 }
 365  1
                 return new String[0];
 366  
             }
 367  
 
 368  
             // Parse this resource into a DOM tree
 369  
             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 370  
             DocumentBuilder db = dbf.newDocumentBuilder();
 371  
             stream = url.openStream();
 372  
             document = db.parse(stream);
 373  
 
 374  
         } catch (Exception e) {
 375  
 
 376  
             if (log().isErrorEnabled()) {
 377  
                 log().error(bundle.getString("mappings.parseWebXml"), e);
 378  
             }
 379  
             return new String[0];
 380  
 
 381  
         } finally {
 382  
 
 383  1
             if (stream != null) {
 384  
                 try { stream.close(); } catch (Exception e) { ; }
 385  
             }
 386  
 
 387  1
         }
 388  
 
 389  
         // Identify the servlet name of the JavaServer Faces controller servlet
 390  
         String name =
 391  
                 context.getExternalContext().getInitParameter(Constants.FACES_SERVLET_NAME_PARAM);
 392  
         if (null == name) {
 393  
             NodeList servletNodes = document.getElementsByTagName("servlet");
 394  
             for (int i = 0; i < servletNodes.getLength(); i++) {
 395  
                 Node servletNode = servletNodes.item(i);
 396  
                 String servletName = null;
 397  
                 String servletClass = null;
 398  
                 NodeList kids = servletNode.getChildNodes();
 399  
                 for (int j = 0; j < kids.getLength(); j++) {
 400  
                     Node kid = kids.item(j);
 401  
                     if ("servlet-name".equals(kid.getNodeName())) {
 402  
                         servletName = text(kid);
 403  
                     } else if ("servlet-class".equals(kid.getNodeName())) {
 404  
                         servletClass = text(kid);
 405  
                     }
 406  
                 }
 407  
                 if ("javax.faces.webapp.FacesServlet".equals(servletClass)) {
 408  
                     name = servletName;
 409  
                 }
 410  
             }
 411  
         }
 412  
 
 413  
         // Identify the URL patterns to which this servlet is mapped
 414  
         List list = new ArrayList();
 415  
         NodeList mappingNodes = document.getElementsByTagName("servlet-mapping");
 416  
         for (int i = 0; i < mappingNodes.getLength(); i++) {
 417  
             Node mappingNode = mappingNodes.item(i);
 418  
             String servletName = null;
 419  
             String urlPattern = null;
 420  
             NodeList kids = mappingNode.getChildNodes();
 421  
             for (int j = 0; j < kids.getLength(); j++) {
 422  
                 Node kid = kids.item(j);
 423  
                 if ("servlet-name".equals(kid.getNodeName())) {
 424  
                     servletName = text(kid);
 425  
                 } else if ("url-pattern".equals(kid.getNodeName())) {
 426  
                     urlPattern = text(kid);
 427  
                 }
 428  
             }
 429  
             if (name.equals(servletName)) {
 430  
                 list.add(urlPattern);
 431  
             }
 432  
         }
 433  
 
 434  
         // Return the resulting list
 435  
         return (String[]) list.toArray(new String[list.size()]);
 436  
 
 437  
     }
 438  
 
 439  
 
 440  
     /**
 441  
      * <p>Return the text content inside the specified node.</p>
 442  
      *
 443  
      * @param node Node from which text is to be extracted
 444  
      */
 445  
     private String text(Node node) {
 446  
 
 447  
         NodeList kids = node.getChildNodes();
 448  
         for (int k = 0; k < kids.getLength(); k++) {
 449  
             Node kid = kids.item(k);
 450  
             if ("#text".equals(kid.getNodeName())) {
 451  
                 return kid.getNodeValue().trim();
 452  
             }
 453  
         }
 454  
         return "";
 455  
 
 456  
     }
 457  
 
 458  
 
 459  
 }