Coverage Report - org.apache.myfaces.application.ResourceHandlerImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
ResourceHandlerImpl
0%
0/593
0%
0/506
11.357
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.myfaces.application;
 20  
 
 21  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
 22  
 import org.apache.myfaces.shared.resource.ResourceHandlerCache;
 23  
 import org.apache.myfaces.shared.resource.ResourceHandlerCache.ResourceValue;
 24  
 import org.apache.myfaces.shared.resource.ResourceHandlerSupport;
 25  
 import org.apache.myfaces.shared.resource.ResourceImpl;
 26  
 import org.apache.myfaces.shared.resource.ResourceLoader;
 27  
 import org.apache.myfaces.shared.resource.ResourceMeta;
 28  
 import org.apache.myfaces.shared.resource.ResourceValidationUtils;
 29  
 import org.apache.myfaces.shared.util.ClassUtils;
 30  
 import org.apache.myfaces.shared.util.ExternalContextUtils;
 31  
 import org.apache.myfaces.shared.util.StringUtils;
 32  
 import org.apache.myfaces.shared.util.WebConfigParamUtils;
 33  
 
 34  
 import javax.faces.application.Resource;
 35  
 import javax.faces.application.ResourceHandler;
 36  
 import javax.faces.application.ResourceWrapper;
 37  
 import javax.faces.context.ExternalContext;
 38  
 import javax.faces.context.FacesContext;
 39  
 import javax.servlet.http.HttpServletResponse;
 40  
 import java.io.IOException;
 41  
 import java.io.InputStream;
 42  
 import java.io.OutputStream;
 43  
 import java.util.List;
 44  
 import java.util.Locale;
 45  
 import java.util.Map;
 46  
 import java.util.MissingResourceException;
 47  
 import java.util.ResourceBundle;
 48  
 import java.util.logging.Level;
 49  
 import java.util.logging.Logger;
 50  
 import java.util.regex.Pattern;
 51  
 import org.apache.myfaces.shared.resource.ContractResource;
 52  
 import org.apache.myfaces.shared.resource.ContractResourceLoader;
 53  
 import org.apache.myfaces.shared.resource.ResourceCachedInfo;
 54  
 
 55  
 /**
 56  
  * DOCUMENT ME!
 57  
  *
 58  
  * @author Simon Lessard (latest modification by $Author$)
 59  
  * 
 60  
  * @version $Revision$ $Date$
 61  
  */
 62  0
 public class ResourceHandlerImpl extends ResourceHandler
 63  
 {
 64  
 
 65  
     private static final String IS_RESOURCE_REQUEST = "org.apache.myfaces.IS_RESOURCE_REQUEST";
 66  
 
 67  
     private ResourceHandlerSupport _resourceHandlerSupport;
 68  
 
 69  
     private ResourceHandlerCache _resourceHandlerCache;
 70  
 
 71  
     //private static final Log log = LogFactory.getLog(ResourceHandlerImpl.class);
 72  0
     private static final Logger log = Logger.getLogger(ResourceHandlerImpl.class.getName());
 73  
 
 74  
     /**
 75  
      * Allow slash in the library name of a Resource. 
 76  
      */
 77  
     @JSFWebConfigParam(since="2.1.6, 2.0.12", defaultValue="false", 
 78  
             expectedValues="true, false", group="resources")
 79  
     public static final String INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME = 
 80  
             "org.apache.myfaces.STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME";
 81  
     public static final boolean INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME_DEFAULT = false;
 82  
     
 83  
     /**
 84  
      * Define the default buffer size that is used between Resource.getInputStream() and 
 85  
      * httpServletResponse.getOutputStream() when rendering resources using the default
 86  
      * ResourceHandler.
 87  
      */
 88  
     @JSFWebConfigParam(since="2.1.10, 2.0.16", defaultValue="2048", group="resources")
 89  
     public static final String INIT_PARAM_RESOURCE_BUFFER_SIZE = "org.apache.myfaces.RESOURCE_BUFFER_SIZE";
 90  
     public static final int INIT_PARAM_RESOURCE_BUFFER_SIZE_DEFAULT = 2048;
 91  
     
 92  0
     public static final Pattern LIBRARY_VERSION_CHECKER = Pattern.compile("\\p{Digit}+(_\\p{Digit}*)*");
 93  0
     public static final Pattern RESOURCE_VERSION_CHECKER = Pattern.compile("\\p{Digit}+(_\\p{Digit}*)*\\..*");    
 94  
     
 95  
     private Boolean _allowSlashLibraryName;
 96  0
     private int _resourceBufferSize = -1;
 97  
     
 98  
     private String[] _excludedResourceExtensions;
 99  
 
 100  
     @Override
 101  
     public Resource createResource(String resourceName)
 102  
     {
 103  0
         return createResource(resourceName, null);
 104  
     }
 105  
 
 106  
     @Override
 107  
     public Resource createResource(String resourceName, String libraryName)
 108  
     {
 109  0
         return createResource(resourceName, libraryName, null);
 110  
     }
 111  
 
 112  
     @Override
 113  
     public Resource createResource(String resourceName, String libraryName,
 114  
             String contentType)
 115  
     {
 116  0
         Resource resource = null;
 117  
         
 118  0
         if (resourceName == null || resourceName.length() == 0) 
 119  
         {
 120  0
             return null;
 121  
         }
 122  0
         if (resourceName.charAt(0) == '/')
 123  
         {
 124  
             // If resourceName starts with '/', remove that character because it
 125  
             // does not have any meaning (with and without should point to the 
 126  
             // same resource).
 127  0
             resourceName = resourceName.substring(1);
 128  
         }        
 129  0
         if (!ResourceValidationUtils.isValidResourceName(resourceName))
 130  
         {
 131  0
             return null;
 132  
         }
 133  0
         if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
 134  
                 libraryName, isAllowSlashesLibraryName()))
 135  
         {
 136  0
             return null;
 137  
         }
 138  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 139  0
         if (contentType == null)
 140  
         {
 141  
             //Resolve contentType using ExternalContext.getMimeType
 142  0
             contentType = facesContext.getExternalContext().getMimeType(resourceName);
 143  
         }
 144  
 
 145  0
         final String localePrefix = getLocalePrefixForLocateResource(facesContext);
 146  0
         final List<String> contracts = facesContext.getResourceLibraryContracts(); 
 147  0
         String contractPreferred = getContractNameForLocateResource(facesContext);
 148  0
         ResourceValue resourceValue = null;
 149  
 
 150  
         // Check cache:
 151  
         //
 152  
         // Contracts are on top of everything, because it is a concept that defines
 153  
         // resources in a application scope concept. It means all resources in
 154  
         // /resources or /META-INF/resources can be overriden using a contract. Note
 155  
         // it also means resources under /META-INF/flows can also be overriden using
 156  
         // a contract.
 157  
         
 158  
         // Check first the preferred contract if any. If not found, try the remaining
 159  
         // contracts and finally if not found try to found a resource without a 
 160  
         // contract name.
 161  0
         if (contractPreferred != null)
 162  
         {
 163  0
             resourceValue = getResourceLoaderCache().getResource(
 164  
                     resourceName, libraryName, contentType, localePrefix, contractPreferred);
 165  
         }
 166  0
         if (resourceValue == null && !contracts.isEmpty())
 167  
         {
 168  
             // Try to get resource but try with a contract name
 169  0
             for (String contract : contracts)
 170  
             {
 171  0
                 resourceValue = getResourceLoaderCache().getResource(
 172  
                     resourceName, libraryName, contentType, localePrefix, contract);
 173  0
                 if (resourceValue != null)
 174  
                 {
 175  0
                     break;
 176  
                 }
 177  0
             }
 178  
         }
 179  
         // Only if no contract preferred try without it.
 180  0
         if (resourceValue == null)
 181  
         {
 182  
             // Try to get resource without contract name
 183  0
             resourceValue = getResourceLoaderCache().getResource(resourceName, libraryName, contentType, localePrefix);
 184  
         }
 185  
         
 186  0
         if(resourceValue != null)
 187  
         {
 188  0
             resource = new ResourceImpl(resourceValue.getResourceMeta(), resourceValue.getResourceLoader(),
 189  
                     getResourceHandlerSupport(), contentType, 
 190  
                     resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getURL() : null, 
 191  
                     resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getRequestPath() : null);
 192  
         }
 193  
         else
 194  
         {
 195  0
             boolean resolved = false;
 196  
             // Try preferred contract first
 197  0
             if (contractPreferred != null)
 198  
             {
 199  0
                 for (ContractResourceLoader loader : getResourceHandlerSupport().getContractResourceLoaders())
 200  
                 {
 201  0
                     ResourceMeta resourceMeta = deriveResourceMeta(loader, resourceName, libraryName, 
 202  
                         localePrefix, contractPreferred);
 203  0
                     if (resourceMeta != null)
 204  
                     {
 205  0
                         resource = new ResourceImpl(resourceMeta, loader, 
 206  
                             getResourceHandlerSupport(), contentType);
 207  
 
 208  
                         // cache it
 209  0
                         getResourceLoaderCache().putResource(resourceName, libraryName, contentType,
 210  
                                 localePrefix, contractPreferred, resourceMeta, loader, 
 211  
                                 new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
 212  0
                         resolved = true;
 213  0
                         break;
 214  
                     }
 215  
                 }
 216  
             }
 217  0
             if (!resolved && !contracts.isEmpty())
 218  
             {
 219  
                 for (ContractResourceLoader loader : 
 220  0
                     getResourceHandlerSupport().getContractResourceLoaders())
 221  
                 {
 222  0
                     for (String contract : contracts)
 223  
                     {
 224  0
                         ResourceMeta resourceMeta = deriveResourceMeta(
 225  
                             loader, resourceName, libraryName, 
 226  
                             localePrefix, contract);
 227  0
                         if (resourceMeta != null)
 228  
                         {
 229  0
                             resource = new ResourceImpl(resourceMeta, loader, 
 230  
                                 getResourceHandlerSupport(), contentType);
 231  
 
 232  
                             // cache it
 233  0
                             getResourceLoaderCache().putResource(
 234  
                                     resourceName, libraryName, contentType,
 235  
                                     localePrefix, contract, resourceMeta, loader,
 236  
                                     new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
 237  0
                             resolved = true;
 238  0
                             break;
 239  
                         }
 240  0
                     }
 241  
                 }
 242  
             }
 243  0
             if (!resolved)
 244  
             {
 245  0
                 for (ResourceLoader loader : getResourceHandlerSupport().getResourceLoaders())
 246  
                 {
 247  0
                     ResourceMeta resourceMeta = deriveResourceMeta(
 248  
                         loader, resourceName, libraryName, localePrefix);
 249  
 
 250  0
                     if (resourceMeta != null)
 251  
                     {
 252  0
                         resource = new ResourceImpl(
 253  
                             resourceMeta, loader, getResourceHandlerSupport(), contentType);
 254  
 
 255  
                         // cache it
 256  0
                         getResourceLoaderCache().putResource(resourceName, libraryName, contentType,
 257  
                                 localePrefix, null, resourceMeta, loader, 
 258  
                                 new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
 259  0
                         break;
 260  
                     }
 261  
                 }
 262  
             }
 263  
         }
 264  0
         return resource;
 265  
     }
 266  
 
 267  
     protected ResourceMeta deriveResourceMeta(ContractResourceLoader resourceLoader,
 268  
             String resourceName, String libraryName, String localePrefix, String contractName)
 269  
     {
 270  0
         String resourceVersion = null;
 271  0
         String libraryVersion = null;
 272  0
         ResourceMeta resourceId = null;
 273  
         
 274  
         //1. Try to locate resource in a localized path
 275  0
         if (localePrefix != null)
 276  
         {
 277  0
             if (null != libraryName)
 278  
             {
 279  0
                 String pathToLib = localePrefix + '/' + libraryName;
 280  0
                 libraryVersion = resourceLoader.getLibraryVersion(pathToLib, contractName);
 281  
 
 282  0
                 if (null != libraryVersion)
 283  
                 {
 284  0
                     String pathToResource = localePrefix + '/'
 285  
                             + libraryName + '/' + libraryVersion + '/'
 286  
                             + resourceName;
 287  0
                     resourceVersion = resourceLoader
 288  
                             .getResourceVersion(pathToResource, contractName);
 289  0
                 }
 290  
                 else
 291  
                 {
 292  0
                     String pathToResource = localePrefix + '/'
 293  
                             + libraryName + '/' + resourceName;
 294  0
                     resourceVersion = resourceLoader
 295  
                             .getResourceVersion(pathToResource, contractName);
 296  
                 }
 297  
 
 298  0
                 if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
 299  
                 {
 300  0
                     resourceId = resourceLoader.createResourceMeta(localePrefix, libraryName,
 301  
                             libraryVersion, resourceName, resourceVersion, contractName);
 302  
                 }
 303  0
             }
 304  
             else
 305  
             {
 306  0
                 resourceVersion = resourceLoader
 307  
                         .getResourceVersion(localePrefix + '/'+ resourceName, contractName);
 308  0
                 if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
 309  
                 {               
 310  0
                     resourceId = resourceLoader.createResourceMeta(localePrefix, null, null,
 311  
                             resourceName, resourceVersion, contractName);
 312  
                 }
 313  
             }
 314  
 
 315  0
             if (resourceId != null && !resourceLoader.resourceExists(resourceId))
 316  
             {
 317  0
                 resourceId = null;
 318  
             }            
 319  
         }
 320  
         
 321  
         //2. Try to localize resource in a non localized path
 322  0
         if (resourceId == null)
 323  
         {
 324  0
             if (null != libraryName)
 325  
             {
 326  0
                 libraryVersion = resourceLoader.getLibraryVersion(libraryName, contractName);
 327  
 
 328  0
                 if (null != libraryVersion)
 329  
                 {
 330  0
                     String pathToResource = (libraryName + '/' + libraryVersion
 331  
                             + '/' + resourceName);
 332  0
                     resourceVersion = resourceLoader
 333  
                             .getResourceVersion(pathToResource, contractName);
 334  0
                 }
 335  
                 else
 336  
                 {
 337  0
                     String pathToResource = (libraryName + '/'
 338  
                             + resourceName);
 339  0
                     resourceVersion = resourceLoader
 340  
                             .getResourceVersion(pathToResource, contractName);
 341  
                 }
 342  
 
 343  0
                 if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
 344  
                 {               
 345  0
                     resourceId = resourceLoader.createResourceMeta(null, libraryName,
 346  
                             libraryVersion, resourceName, resourceVersion, contractName);
 347  
                 }
 348  
             }
 349  
             else
 350  
             {
 351  0
                 resourceVersion = resourceLoader
 352  
                         .getResourceVersion(resourceName, contractName);
 353  
                 
 354  0
                 if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
 355  
                 {               
 356  0
                     resourceId = resourceLoader.createResourceMeta(null, null, null,
 357  
                             resourceName, resourceVersion, contractName);
 358  
                 }
 359  
             }
 360  
 
 361  0
             if (resourceId != null && !resourceLoader.resourceExists(resourceId))
 362  
             {
 363  0
                 resourceId = null;
 364  
             }            
 365  
         }
 366  
         
 367  0
         return resourceId;
 368  
     }
 369  
     
 370  
     /**
 371  
      * This method try to create a ResourceMeta for a specific resource
 372  
      * loader. If no library, or resource is found, just return null,
 373  
      * so the algorithm in createResource can continue checking with the 
 374  
      * next registered ResourceLoader. 
 375  
      */
 376  
     protected ResourceMeta deriveResourceMeta(ResourceLoader resourceLoader,
 377  
             String resourceName, String libraryName, String localePrefix)
 378  
     {
 379  0
         String resourceVersion = null;
 380  0
         String libraryVersion = null;
 381  0
         ResourceMeta resourceId = null;
 382  
         
 383  
         //1. Try to locate resource in a localized path
 384  0
         if (localePrefix != null)
 385  
         {
 386  0
             if (null != libraryName)
 387  
             {
 388  0
                 String pathToLib = localePrefix + '/' + libraryName;
 389  0
                 libraryVersion = resourceLoader.getLibraryVersion(pathToLib);
 390  
 
 391  0
                 if (null != libraryVersion)
 392  
                 {
 393  0
                     String pathToResource = localePrefix + '/'
 394  
                             + libraryName + '/' + libraryVersion + '/'
 395  
                             + resourceName;
 396  0
                     resourceVersion = resourceLoader
 397  
                             .getResourceVersion(pathToResource);
 398  0
                 }
 399  
                 else
 400  
                 {
 401  0
                     String pathToResource = localePrefix + '/'
 402  
                             + libraryName + '/' + resourceName;
 403  0
                     resourceVersion = resourceLoader
 404  
                             .getResourceVersion(pathToResource);
 405  
                 }
 406  
 
 407  0
                 if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
 408  
                 {
 409  0
                     resourceId = resourceLoader.createResourceMeta(localePrefix, libraryName,
 410  
                             libraryVersion, resourceName, resourceVersion);
 411  
                 }
 412  0
             }
 413  
             else
 414  
             {
 415  0
                 resourceVersion = resourceLoader
 416  
                         .getResourceVersion(localePrefix + '/'+ resourceName);
 417  0
                 if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
 418  
                 {               
 419  0
                     resourceId = resourceLoader.createResourceMeta(localePrefix, null, null,
 420  
                             resourceName, resourceVersion);
 421  
                 }
 422  
             }
 423  
 
 424  0
             if (resourceId != null && !resourceLoader.resourceExists(resourceId))
 425  
             {
 426  0
                 resourceId = null;
 427  
             }            
 428  
         }
 429  
         
 430  
         //2. Try to localize resource in a non localized path
 431  0
         if (resourceId == null)
 432  
         {
 433  0
             if (null != libraryName)
 434  
             {
 435  0
                 libraryVersion = resourceLoader.getLibraryVersion(libraryName);
 436  
 
 437  0
                 if (null != libraryVersion)
 438  
                 {
 439  0
                     String pathToResource = (libraryName + '/' + libraryVersion
 440  
                             + '/' + resourceName);
 441  0
                     resourceVersion = resourceLoader
 442  
                             .getResourceVersion(pathToResource);
 443  0
                 }
 444  
                 else
 445  
                 {
 446  0
                     String pathToResource = (libraryName + '/'
 447  
                             + resourceName);
 448  0
                     resourceVersion = resourceLoader
 449  
                             .getResourceVersion(pathToResource);
 450  
                 }
 451  
 
 452  0
                 if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
 453  
                 {               
 454  0
                     resourceId = resourceLoader.createResourceMeta(null, libraryName,
 455  
                             libraryVersion, resourceName, resourceVersion);
 456  
                 }
 457  
             }
 458  
             else
 459  
             {
 460  0
                 resourceVersion = resourceLoader
 461  
                         .getResourceVersion(resourceName);
 462  
                 
 463  0
                 if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
 464  
                 {               
 465  0
                     resourceId = resourceLoader.createResourceMeta(null, null, null,
 466  
                             resourceName, resourceVersion);
 467  
                 }
 468  
             }
 469  
 
 470  0
             if (resourceId != null && !resourceLoader.resourceExists(resourceId))
 471  
             {
 472  0
                 resourceId = null;
 473  
             }            
 474  
         }
 475  
         
 476  0
         return resourceId;
 477  
     }
 478  
 
 479  
     @Override
 480  
     public String getRendererTypeForResourceName(String resourceName)
 481  
     {
 482  0
         if (resourceName.endsWith(".js"))
 483  
         {
 484  0
             return "javax.faces.resource.Script";
 485  
         }
 486  0
         else if (resourceName.endsWith(".css"))
 487  
         {
 488  0
             return "javax.faces.resource.Stylesheet";
 489  
         }
 490  0
         return null;
 491  
     }
 492  
 
 493  
     /**
 494  
      *  Handle the resource request, writing in the output. 
 495  
      *  
 496  
      *  This method implements an algorithm semantically identical to 
 497  
      *  the one described on the javadoc of ResourceHandler.handleResourceRequest 
 498  
      */
 499  
     @Override
 500  
     public void handleResourceRequest(FacesContext facesContext) throws IOException
 501  
     {
 502  
         //try
 503  
         //{
 504  0
             String resourceBasePath = getResourceHandlerSupport()
 505  
                     .calculateResourceBasePath(facesContext);
 506  
     
 507  0
             if (resourceBasePath == null)
 508  
             {
 509  
                 // No base name could be calculated, so no further
 510  
                 //advance could be done here. HttpServletResponse.SC_NOT_FOUND
 511  
                 //cannot be returned since we cannot extract the 
 512  
                 //resource base name
 513  0
                 return;
 514  
             }
 515  
     
 516  
             // We neet to get an instance of HttpServletResponse, but sometimes
 517  
             // the response object is wrapped by several instances of 
 518  
             // ServletResponseWrapper (like ResponseSwitch).
 519  
             // Since we are handling a resource, we can expect to get an 
 520  
             // HttpServletResponse.
 521  0
             ExternalContext extContext = facesContext.getExternalContext();
 522  0
             Object response = extContext.getResponse();
 523  0
             HttpServletResponse httpServletResponse = ExternalContextUtils.getHttpServletResponse(response);
 524  0
             if (httpServletResponse == null)
 525  
             {
 526  0
                 throw new IllegalStateException("Could not obtain an instance of HttpServletResponse.");
 527  
             }
 528  
     
 529  0
             if (isResourceIdentifierExcluded(facesContext, resourceBasePath))
 530  
             {
 531  0
                 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
 532  0
                 return;
 533  
             }
 534  
     
 535  0
             String resourceName = null;
 536  0
             if (resourceBasePath.startsWith(ResourceHandler.RESOURCE_IDENTIFIER))
 537  
             {
 538  0
                 resourceName = resourceBasePath
 539  
                         .substring(ResourceHandler.RESOURCE_IDENTIFIER.length() + 1);
 540  
                 
 541  0
                 if (resourceBasePath != null && !ResourceValidationUtils.isValidResourceName(resourceName))
 542  
                 {
 543  0
                     httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
 544  0
                     return;
 545  
                 }
 546  
             }
 547  
             else
 548  
             {
 549  
                 //Does not have the conditions for be a resource call
 550  0
                 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
 551  0
                 return;
 552  
             }
 553  
     
 554  0
             String libraryName = facesContext.getExternalContext()
 555  
                     .getRequestParameterMap().get("ln");
 556  
     
 557  0
             if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
 558  
                     libraryName, isAllowSlashesLibraryName()))
 559  
             {
 560  0
                 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
 561  0
                 return;
 562  
             }
 563  
             
 564  0
             Resource resource = null;
 565  0
             if (libraryName != null)
 566  
             {
 567  
                 //log.info("libraryName=" + libraryName);
 568  0
                 resource = facesContext.getApplication().getResourceHandler().createResource(resourceName, libraryName);
 569  
             }
 570  
             else
 571  
             {
 572  0
                 resource = facesContext.getApplication().getResourceHandler().createResource(resourceName);
 573  
             }
 574  
     
 575  0
             if (resource == null)
 576  
             {
 577  0
                 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
 578  0
                 return;
 579  
             }
 580  
     
 581  0
             if (!resource.userAgentNeedsUpdate(facesContext))
 582  
             {
 583  0
                 httpServletResponse.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
 584  0
                 return;
 585  
             }
 586  
     
 587  0
             httpServletResponse.setContentType(_getContentType(resource, facesContext.getExternalContext()));
 588  
     
 589  0
             Map<String, String> headers = resource.getResponseHeaders();
 590  
     
 591  0
             for (Map.Entry<String, String> entry : headers.entrySet())
 592  
             {
 593  0
                 httpServletResponse.setHeader(entry.getKey(), entry.getValue());
 594  0
             }
 595  
     
 596  
             // Sets the preferred buffer size for the body of the response
 597  0
             extContext.setResponseBufferSize(this.getResourceBufferSize());
 598  
             
 599  
             //serve up the bytes (taken from trinidad ResourceServlet)
 600  
             try
 601  
             {
 602  0
                 InputStream in = resource.getInputStream();
 603  0
                 OutputStream out = httpServletResponse.getOutputStream();
 604  
                 //byte[] buffer = new byte[_BUFFER_SIZE];
 605  0
                 byte[] buffer = new byte[this.getResourceBufferSize()];
 606  
     
 607  
                 try
 608  
                 {
 609  0
                     int count = pipeBytes(in, out, buffer);
 610  
                     //set the content lenght
 611  0
                     if (!httpServletResponse.isCommitted())
 612  
                     {
 613  0
                         httpServletResponse.setContentLength(count);
 614  
                     }
 615  
                 }
 616  
                 finally
 617  
                 {
 618  0
                     try
 619  
                     {
 620  0
                         in.close();
 621  
                     }
 622  
                     finally
 623  
                     {
 624  0
                         out.close();
 625  0
                     }
 626  0
                 }
 627  
             }
 628  0
             catch (IOException e)
 629  
             {
 630  
                 //TODO: Log using a localized message (which one?)
 631  0
                 if (isConnectionAbort(e))
 632  
                 {
 633  0
                     log.log(Level.INFO,"Connection was aborted while loading resource " + resourceName
 634  
                             + " with library " + libraryName);
 635  
                 }
 636  
                 else
 637  
                 {
 638  0
                     if (log.isLoggable(Level.WARNING))
 639  
                     {
 640  0
                         log.log(Level.WARNING,"Error trying to load and send resource " + resourceName
 641  
                                 + " with library " + libraryName + " :"
 642  
                                 + e.getMessage(), e);
 643  
                     }
 644  0
                     httpServletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);
 645  
                 }
 646  0
             }
 647  
         //}
 648  
         //catch (Throwable ex)
 649  
         //{
 650  
             // handle the Throwable accordingly. Maybe generate an error page.
 651  
             // FIXME we are creating a html error page for a non html request here
 652  
             // shouln't we do something better? -=Jakob Korherr=-
 653  
             //ErrorPageWriter.handleThrowable(facesContext, ex);
 654  
         //}
 655  0
     }
 656  
 
 657  
     private static boolean isConnectionAbort(IOException e)
 658  
     {
 659  0
         return e.getClass().getCanonicalName().equals("org.apache.catalina.connector.ClientAbortException");
 660  
     }
 661  
 
 662  
     /**
 663  
      * Reads the specified input stream into the provided byte array storage and
 664  
      * writes it to the output stream.
 665  
      */
 666  
     private static int pipeBytes(InputStream in, OutputStream out, byte[] buffer)
 667  
             throws IOException
 668  
     {
 669  0
         int count = 0;
 670  
         int length;
 671  
 
 672  0
         while ((length = (in.read(buffer))) >= 0)
 673  
         {
 674  0
             out.write(buffer, 0, length);
 675  0
             count += length;
 676  
         }
 677  0
         return count;
 678  
     }
 679  
 
 680  
     @Override
 681  
     public boolean isResourceRequest(FacesContext facesContext)
 682  
     {
 683  
         // Since this method could be called many times we save it
 684  
         // on request map so the first time is calculated it remains
 685  
         // alive until the end of the request
 686  0
         Boolean value = (Boolean) facesContext.getAttributes().get(IS_RESOURCE_REQUEST);
 687  
 
 688  0
         if (value == null)
 689  
         {
 690  0
             String resourceBasePath = getResourceHandlerSupport()
 691  
                     .calculateResourceBasePath(facesContext);
 692  
 
 693  0
             value = resourceBasePath != null
 694  
                     && resourceBasePath.startsWith(ResourceHandler.RESOURCE_IDENTIFIER);
 695  0
             facesContext.getAttributes().put(IS_RESOURCE_REQUEST, value);
 696  
         }
 697  0
         return value;
 698  
     }
 699  
 
 700  
     protected String getLocalePrefixForLocateResource()
 701  
     {
 702  0
         return getLocalePrefixForLocateResource(FacesContext.getCurrentInstance());
 703  
     }
 704  
 
 705  
     protected String getLocalePrefixForLocateResource(FacesContext context)
 706  
     {
 707  0
         String localePrefix = null;
 708  0
         boolean isResourceRequest = context.getApplication().getResourceHandler().isResourceRequest(context);
 709  
 
 710  0
         if (isResourceRequest)
 711  
         {
 712  0
             localePrefix = context.getExternalContext().getRequestParameterMap().get("loc");
 713  
             
 714  0
             if (localePrefix != null)
 715  
             {
 716  0
                 if (!ResourceValidationUtils.isValidLocalePrefix(localePrefix))
 717  
                 {
 718  0
                     return null;
 719  
                 }
 720  0
                 return localePrefix;
 721  
             }
 722  
         }
 723  
         
 724  0
         String bundleName = context.getApplication().getMessageBundle();
 725  
 
 726  0
         if (null != bundleName)
 727  
         {
 728  0
             Locale locale = null;
 729  
             
 730  0
             if (isResourceRequest || context.getViewRoot() == null)
 731  
             {
 732  0
                 locale = context.getApplication().getViewHandler()
 733  
                                 .calculateLocale(context);
 734  
             }
 735  
             else
 736  
             {
 737  0
                 locale = context.getViewRoot().getLocale();
 738  
             }
 739  
 
 740  
             try
 741  
             {
 742  0
                 ResourceBundle bundle = ResourceBundle
 743  
                         .getBundle(bundleName, locale, ClassUtils.getContextClassLoader());
 744  
 
 745  0
                 if (bundle != null)
 746  
                 {
 747  0
                     if (bundle.containsKey(ResourceHandler.LOCALE_PREFIX))
 748  
                     {
 749  0
                         localePrefix = bundle.getString(ResourceHandler.LOCALE_PREFIX);
 750  
                     }
 751  
                 }
 752  
             }
 753  0
             catch (MissingResourceException e)
 754  
             {
 755  
                 // Ignore it and return null
 756  0
             }
 757  
         }
 758  0
         return localePrefix;
 759  
     }
 760  
     
 761  
     protected String getContractNameForLocateResource(FacesContext context)
 762  
     {
 763  0
         String contractName = null;
 764  0
         boolean isResourceRequest = context.getApplication().getResourceHandler().isResourceRequest(context);
 765  
 
 766  0
         if (isResourceRequest)
 767  
         {
 768  0
             contractName = context.getExternalContext().getRequestParameterMap().get("con");
 769  
         }
 770  
         
 771  
         // Check if the contract has been injected.
 772  0
         if (contractName == null)
 773  
         {
 774  0
             contractName = (String) context.getAttributes().get(ContractResource.CONTRACT_SELECTED);
 775  
         }
 776  
         
 777  
         //Validate
 778  0
         if (contractName != null &&
 779  
             !ResourceValidationUtils.isValidContractName(contractName))
 780  
         {
 781  0
             return null;
 782  
         }
 783  0
         return contractName;
 784  
     }
 785  
 
 786  
     protected boolean isResourceIdentifierExcluded(FacesContext context, String resourceIdentifier)
 787  
     {
 788  0
         if (_excludedResourceExtensions == null)
 789  
         {
 790  0
             String value = WebConfigParamUtils.getStringInitParameter(context.getExternalContext(),
 791  
                             RESOURCE_EXCLUDES_PARAM_NAME,
 792  
                             RESOURCE_EXCLUDES_DEFAULT_VALUE);
 793  
             
 794  0
             _excludedResourceExtensions = StringUtils.splitShortString(value, ' ');
 795  
         }
 796  
         
 797  0
         for (int i = 0; i < _excludedResourceExtensions.length; i++)
 798  
         {
 799  0
             if (resourceIdentifier.endsWith(_excludedResourceExtensions[i]))
 800  
             {
 801  0
                 return true;
 802  
             }
 803  
         }
 804  0
         return false;
 805  
     }
 806  
 
 807  
     /**
 808  
      * Check if a library exists or not. This is done delegating
 809  
      * to each ResourceLoader used, because each one has a different
 810  
      * prefix and way to load resources.
 811  
      * 
 812  
      */
 813  
     @Override
 814  
     public boolean libraryExists(String libraryName)
 815  
     {
 816  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 817  0
         String localePrefix = getLocalePrefixForLocateResource(facesContext);
 818  0
         final List<String> contracts = facesContext.getResourceLibraryContracts(); 
 819  
 
 820  0
         String pathToLib = null;
 821  0
         Boolean libraryFound = null;
 822  0
         if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
 823  
                 libraryName, isAllowSlashesLibraryName()))
 824  
         {
 825  0
             return false;
 826  
         }
 827  
         
 828  0
         if (localePrefix != null)
 829  
         {
 830  
             //Check with locale
 831  0
             pathToLib = localePrefix + '/' + libraryName;
 832  
 
 833  0
             libraryFound = getResourceLoaderCache().libraryExists(pathToLib);
 834  0
             if (libraryFound != null)
 835  
             {
 836  0
                 return libraryFound.booleanValue();
 837  
             }
 838  
         }
 839  0
         libraryFound = getResourceLoaderCache().libraryExists(libraryName);
 840  0
         if (libraryFound != null)
 841  
         {
 842  0
             return libraryFound.booleanValue();
 843  
         }
 844  
         
 845  0
         if (localePrefix != null)
 846  
         {
 847  0
             if (!contracts.isEmpty())
 848  
             {
 849  0
                 for (String contract : contracts)
 850  
                 {
 851  0
                     for (ContractResourceLoader loader : getResourceHandlerSupport()
 852  
                             .getContractResourceLoaders())
 853  
                     {
 854  0
                         if (loader.libraryExists(pathToLib, contract))
 855  
                         {
 856  0
                             getResourceLoaderCache().confirmLibraryExists(pathToLib);
 857  0
                             return true;
 858  
                         }
 859  
                     }
 860  0
                 }
 861  
             }
 862  
             
 863  0
             for (ResourceLoader loader : getResourceHandlerSupport()
 864  
                     .getResourceLoaders())
 865  
             {
 866  0
                 if (loader.libraryExists(pathToLib))
 867  
                 {
 868  0
                     getResourceLoaderCache().confirmLibraryExists(pathToLib);
 869  0
                     return true;
 870  
                 }
 871  
             }            
 872  
         }
 873  
 
 874  
         //Check without locale
 875  0
         if (!contracts.isEmpty())
 876  
         {
 877  0
             for (String contract : contracts)
 878  
             {
 879  0
                 for (ContractResourceLoader loader : getResourceHandlerSupport()
 880  
                         .getContractResourceLoaders())
 881  
                 {
 882  0
                     if (loader.libraryExists(libraryName, contract))
 883  
                     {
 884  0
                         getResourceLoaderCache().confirmLibraryExists(libraryName);
 885  0
                         return true;
 886  
                     }
 887  
                 }
 888  0
             }
 889  
         }
 890  
 
 891  0
         for (ResourceLoader loader : getResourceHandlerSupport()
 892  
                 .getResourceLoaders())
 893  
         {
 894  0
             if (loader.libraryExists(libraryName))
 895  
             {
 896  0
                 getResourceLoaderCache().confirmLibraryExists(libraryName);
 897  0
                 return true;
 898  
             }
 899  
         }
 900  
 
 901  0
         if (localePrefix != null)
 902  
         {
 903  
             //Check with locale
 904  0
             getResourceLoaderCache().confirmLibraryNotExists(pathToLib);
 905  
         }
 906  
         else
 907  
         {
 908  0
             getResourceLoaderCache().confirmLibraryNotExists(libraryName);
 909  
         }
 910  0
         return false;
 911  
     }
 912  
 
 913  
     /**
 914  
      * @param resourceHandlerSupport
 915  
      *            the resourceHandlerSupport to set
 916  
      */
 917  
     public void setResourceHandlerSupport(
 918  
             ResourceHandlerSupport resourceHandlerSupport)
 919  
     {
 920  0
         _resourceHandlerSupport = resourceHandlerSupport;
 921  0
     }
 922  
 
 923  
     /**
 924  
      * @return the resourceHandlerSupport
 925  
      */
 926  
     protected ResourceHandlerSupport getResourceHandlerSupport()
 927  
     {
 928  0
         if (_resourceHandlerSupport == null)
 929  
         {
 930  0
             _resourceHandlerSupport = new DefaultResourceHandlerSupport();
 931  
         }
 932  0
         return _resourceHandlerSupport;
 933  
     }
 934  
 
 935  
     private ResourceHandlerCache getResourceLoaderCache()
 936  
     {
 937  0
         if (_resourceHandlerCache == null)
 938  
         {
 939  0
             _resourceHandlerCache = new ResourceHandlerCache();
 940  
         }
 941  0
         return _resourceHandlerCache;
 942  
     }
 943  
 
 944  
     private String _getContentType(Resource resource, ExternalContext externalContext)
 945  
     {
 946  0
         String contentType = resource.getContentType();
 947  
 
 948  
         // the resource does not provide a content-type --> determine it via mime-type
 949  0
         if (contentType == null || contentType.length() == 0)
 950  
         {
 951  0
             String resourceName = getWrappedResourceName(resource);
 952  
 
 953  0
             if (resourceName != null)
 954  
             {
 955  0
                 contentType = externalContext.getMimeType(resourceName);
 956  
             }
 957  
         }
 958  
 
 959  0
         return contentType;
 960  
     }
 961  
 
 962  
     /**
 963  
      * Recursively unwarp the resource until we find the real resourceName
 964  
      * This is needed because the JSF2 specced ResourceWrapper doesn't override
 965  
      * the getResourceName() method :(
 966  
      * @param resource
 967  
      * @return the first non-null resourceName or <code>null</code> if none set
 968  
      */
 969  
     private String getWrappedResourceName(Resource resource)
 970  
     {
 971  0
         String resourceName = resource.getResourceName();
 972  0
         if (resourceName != null)
 973  
         {
 974  0
             return resourceName;
 975  
         }
 976  
 
 977  0
         if (resource instanceof ResourceWrapper)
 978  
         {
 979  0
             return getWrappedResourceName(((ResourceWrapper) resource).getWrapped());
 980  
         }
 981  
 
 982  0
         return null;
 983  
     }
 984  
     
 985  
     protected boolean isAllowSlashesLibraryName()
 986  
     {
 987  0
         if (_allowSlashLibraryName == null)
 988  
         {
 989  0
             _allowSlashLibraryName = WebConfigParamUtils.getBooleanInitParameter(
 990  
                     FacesContext.getCurrentInstance().getExternalContext(), 
 991  
                     INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME,
 992  
                     INIT_PARAM_STRICT_JSF_2_ALLOW_SLASH_LIBRARY_NAME_DEFAULT);
 993  
         }
 994  0
         return _allowSlashLibraryName;
 995  
     }
 996  
 
 997  
     protected int getResourceBufferSize()
 998  
     {
 999  0
         if (_resourceBufferSize == -1)
 1000  
         {
 1001  0
             _resourceBufferSize = WebConfigParamUtils.getIntegerInitParameter(
 1002  
                 FacesContext.getCurrentInstance().getExternalContext(),
 1003  
                 INIT_PARAM_RESOURCE_BUFFER_SIZE,
 1004  
                 INIT_PARAM_RESOURCE_BUFFER_SIZE_DEFAULT);
 1005  
         }
 1006  0
         return _resourceBufferSize;
 1007  
     }
 1008  
 
 1009  
     @Override
 1010  
     public Resource createResourceFromId(String resourceId)
 1011  
     {
 1012  0
         Resource resource = null;
 1013  
 
 1014  0
         if (resourceId == null)
 1015  
         {
 1016  0
             throw new NullPointerException();
 1017  
         }
 1018  
         
 1019  
         // Later in deriveResourceMeta the resourceId is decomposed and
 1020  
         // its elements validated properly.
 1021  0
         if (!ResourceValidationUtils.isValidResourceId(resourceId))
 1022  
         {
 1023  0
             return null;
 1024  
         }
 1025  
         
 1026  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 1027  0
         final List<String> contracts = facesContext.getResourceLibraryContracts(); 
 1028  0
         String contractPreferred = getContractNameForLocateResource(facesContext);
 1029  0
         ResourceValue resourceValue = null;
 1030  
         
 1031  
         // Check cache:
 1032  
         //
 1033  
         // Contracts are on top of everything, because it is a concept that defines
 1034  
         // resources in a application scope concept. It means all resources in
 1035  
         // /resources or /META-INF/resources can be overriden using a contract. Note
 1036  
         // it also means resources under /META-INF/flows can also be overriden using
 1037  
         // a contract.
 1038  0
         if (contractPreferred != null)
 1039  
         {
 1040  0
             resourceValue = getResourceLoaderCache().getResource(
 1041  
                     resourceId, contractPreferred);
 1042  
         }
 1043  0
         if (resourceValue == null && !contracts.isEmpty())
 1044  
         {
 1045  
             // Try to get resource but try with a contract name
 1046  0
             for (String contract : contracts)
 1047  
             {
 1048  0
                 resourceValue = getResourceLoaderCache().getResource(resourceId, contract);
 1049  0
                 if (resourceValue != null)
 1050  
                 {
 1051  0
                     break;
 1052  
                 }
 1053  0
             }
 1054  
         }
 1055  0
         if (resourceValue == null)
 1056  
         {
 1057  
             // Try to get resource without contract name
 1058  0
             resourceValue = getResourceLoaderCache().getResource(resourceId);
 1059  
         }
 1060  
         
 1061  0
         if(resourceValue != null)
 1062  
         {        
 1063  
             //Resolve contentType using ExternalContext.getMimeType
 1064  0
             String contentType = facesContext.getExternalContext().getMimeType(
 1065  
                 resourceValue.getResourceMeta().getResourceName());
 1066  
 
 1067  0
             resource = new ResourceImpl(resourceValue.getResourceMeta(), resourceValue.getResourceLoader(),
 1068  
                     getResourceHandlerSupport(), contentType,
 1069  
                     resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getURL() : null, 
 1070  
                     resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getRequestPath() : null);
 1071  0
         }
 1072  
         else
 1073  
         {
 1074  0
             boolean resolved = false;
 1075  0
             if (contractPreferred != null)
 1076  
             {
 1077  0
                 for (ContractResourceLoader loader : getResourceHandlerSupport().getContractResourceLoaders())
 1078  
                 {
 1079  0
                     ResourceMeta resourceMeta = deriveResourceMeta(
 1080  
                         facesContext, loader, resourceId, contractPreferred);
 1081  0
                     if (resourceMeta != null)
 1082  
                     {
 1083  0
                         String contentType = facesContext.getExternalContext().getMimeType(
 1084  
                             resourceMeta.getResourceName());
 1085  
                         
 1086  0
                         resource = new ResourceImpl(resourceMeta, loader, 
 1087  
                             getResourceHandlerSupport(), contentType);
 1088  
 
 1089  
                         // cache it
 1090  0
                         getResourceLoaderCache().putResource(resourceId, resourceMeta, loader, 
 1091  
                             new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
 1092  
                         
 1093  0
                         resolved = true;
 1094  0
                         break;
 1095  
                     }
 1096  
                 }
 1097  
             }
 1098  0
             if (!resolved && !contracts.isEmpty())
 1099  
             {
 1100  
                 for (ContractResourceLoader loader : 
 1101  0
                         getResourceHandlerSupport().getContractResourceLoaders())
 1102  
                 {
 1103  0
                     for (String contract : contracts)
 1104  
                     {
 1105  0
                         ResourceMeta resourceMeta = deriveResourceMeta(
 1106  
                             facesContext, loader, resourceId, contract);
 1107  0
                         if (resourceMeta != null)
 1108  
                         {
 1109  0
                             String contentType = facesContext.getExternalContext().getMimeType(
 1110  
                                 resourceMeta.getResourceName());
 1111  
 
 1112  0
                             resource = new ResourceImpl(resourceMeta, loader, 
 1113  
                                 getResourceHandlerSupport(), contentType);
 1114  
 
 1115  
                             // cache it
 1116  0
                             getResourceLoaderCache().putResource(resourceId, resourceMeta, loader, 
 1117  
                                 new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
 1118  
 
 1119  0
                             resolved = true;
 1120  0
                             break;
 1121  
                         }
 1122  0
                     }
 1123  
                 }
 1124  
             }
 1125  0
             if (!resolved)
 1126  
             {
 1127  0
                 for (ResourceLoader loader : getResourceHandlerSupport().getResourceLoaders())
 1128  
                 {
 1129  0
                     ResourceMeta resourceMeta = deriveResourceMeta(facesContext, loader, resourceId);
 1130  
 
 1131  0
                     if (resourceMeta != null)
 1132  
                     {
 1133  0
                         String contentType = facesContext.getExternalContext().getMimeType(
 1134  
                             resourceMeta.getResourceName());
 1135  
 
 1136  0
                         resource = new ResourceImpl(resourceMeta, loader, getResourceHandlerSupport(), contentType);
 1137  
 
 1138  
                         // cache it
 1139  0
                         getResourceLoaderCache().putResource(resourceId, resourceMeta, loader, 
 1140  
                             new ResourceCachedInfo(resource.getURL(), resource.getRequestPath()));
 1141  0
                         break;
 1142  
                     }
 1143  
                 }
 1144  
             }
 1145  
         }
 1146  0
         return resource;
 1147  
     }
 1148  
 
 1149  
     protected ResourceMeta deriveResourceMeta(FacesContext context, ResourceLoader resourceLoader,
 1150  
             String resourceId)
 1151  
     {
 1152  0
         ResourceMeta resourceMeta = null;
 1153  0
         String token = null;
 1154  0
         String localePrefix = null;
 1155  0
         String libraryName = null;
 1156  0
         String libraryVersion = null;
 1157  0
         String resourceName = null;
 1158  0
         String resourceVersion = null;
 1159  
         
 1160  
         // Check if resource exists. It avoids additional 
 1161  
         // checks and it can be done very quickly because the 
 1162  
         // loader always uses the resourceId structure to
 1163  
         // organize resources. But decompose the resourceId is
 1164  
         // even faster.
 1165  
         //if (resourceLoader.resourceIdExists(resourceId))
 1166  
         //{
 1167  0
         int lastSlash = resourceId.lastIndexOf('/');
 1168  0
         if (lastSlash < 0)
 1169  
         {
 1170  
             //no slashes, so it is just a plain resource.
 1171  0
             resourceName = resourceId;
 1172  
         }
 1173  
         else
 1174  
         {
 1175  0
             token = resourceId.substring(lastSlash+1);
 1176  0
             if (RESOURCE_VERSION_CHECKER.matcher(token).matches())
 1177  
             {
 1178  0
                 int secondLastSlash = resourceId.lastIndexOf('/', lastSlash-1);
 1179  0
                 if (secondLastSlash < 0)
 1180  
                 {
 1181  0
                     secondLastSlash = 0;
 1182  
                 }
 1183  
 
 1184  0
                 String rnToken = resourceId.substring(secondLastSlash+1, lastSlash);
 1185  0
                 int lastPoint = rnToken.lastIndexOf('.');
 1186  
                 // lastPoint < 0 means it does not match, the token is not a resource version
 1187  0
                 if (lastPoint >= 0)
 1188  
                 {
 1189  0
                     String ext = rnToken.substring(lastPoint);
 1190  0
                     if (token.endsWith(ext))
 1191  
                     {
 1192  
                         //It match a versioned resource
 1193  0
                         resourceVersion = token.substring(0,token.length()-ext.length());
 1194  
                     }
 1195  
                 }
 1196  
             }
 1197  
 
 1198  
             // 1. Extract the library path and locale prefix if necessary
 1199  0
             int start = 0;
 1200  0
             int firstSlash = resourceId.indexOf('/');
 1201  
 
 1202  
             // At least one slash, check if the start is locale prefix.
 1203  0
             String bundleName = context.getApplication().getMessageBundle();
 1204  
             //If no bundle set, it can't be localePrefix
 1205  0
             if (null != bundleName)
 1206  
             {
 1207  0
                 token = resourceId.substring(start, firstSlash);
 1208  
                 //Try to derive a locale object
 1209  0
                 Locale locale = _LocaleUtils.deriveLocale(token);
 1210  
 
 1211  
                 // If the locale was derived and it is available, 
 1212  
                 // assume that portion of the resourceId it as a locale prefix.
 1213  0
                 if (locale != null && _LocaleUtils.isAvailableLocale(locale))
 1214  
                 {
 1215  0
                     localePrefix = token;
 1216  0
                     start = firstSlash+1;
 1217  
                 }
 1218  
             }
 1219  
 
 1220  
             //Check slash again from start
 1221  0
             firstSlash = resourceId.indexOf('/', start);
 1222  0
             if (firstSlash < 0)
 1223  
             {
 1224  
                 //no slashes.
 1225  0
                 resourceName = resourceId.substring(start);
 1226  
             }
 1227  
             else
 1228  
             {
 1229  
                 //check libraryName
 1230  0
                 token = resourceId.substring(start, firstSlash);
 1231  0
                 int minResourceNameSlash = (resourceVersion != null) ?
 1232  
                     resourceId.lastIndexOf('/', lastSlash-1) : lastSlash;
 1233  
                 //if (resourceLoader.libraryExists(token))
 1234  0
                 if (start < minResourceNameSlash)
 1235  
                 {
 1236  0
                     libraryName = token;
 1237  0
                     start = firstSlash+1;
 1238  
 
 1239  
                     //Now that libraryName exists, check libraryVersion
 1240  0
                     firstSlash = resourceId.indexOf('/', start);
 1241  0
                     if (firstSlash >= 0)
 1242  
                     {
 1243  0
                         token = resourceId.substring(start, firstSlash);
 1244  0
                         if (LIBRARY_VERSION_CHECKER.matcher(token).matches())
 1245  
                         {
 1246  0
                             libraryVersion = token;
 1247  0
                             start = firstSlash+1;
 1248  
                         }
 1249  
                     }
 1250  
                 }
 1251  
 
 1252  0
                 firstSlash = resourceId.indexOf('/', start);
 1253  0
                 if (firstSlash < 0)
 1254  
                 {
 1255  
                     //no slashes.
 1256  0
                     resourceName = resourceId.substring(start);
 1257  
                 }
 1258  
                 else
 1259  
                 {
 1260  
                     // Check resource version. 
 1261  0
                     if (resourceVersion != null)
 1262  
                     {
 1263  0
                         resourceName = resourceId.substring(start,lastSlash);
 1264  
                     }
 1265  
                     else
 1266  
                     {
 1267  
                         //no resource version, assume the remaining to be resource name
 1268  0
                         resourceName = resourceId.substring(start);
 1269  
                     }
 1270  
                 }
 1271  
             }
 1272  
         }
 1273  
 
 1274  
         //Check libraryName and resourceName
 1275  0
         if (resourceName == null)
 1276  
         {
 1277  0
             return null;
 1278  
         }
 1279  0
         if (!ResourceValidationUtils.isValidResourceName(resourceName))
 1280  
         {
 1281  0
             return null;
 1282  
         }
 1283  
 
 1284  0
         if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
 1285  
                 libraryName, isAllowSlashesLibraryName()))
 1286  
         {
 1287  0
             return null;
 1288  
         }
 1289  
 
 1290  
         // If some variable is "" set it as null.
 1291  0
         if (localePrefix != null && localePrefix.length() == 0)
 1292  
         {
 1293  0
             localePrefix = null;
 1294  
         }
 1295  0
         if (libraryName != null && libraryName.length() == 0)
 1296  
         {
 1297  0
             libraryName = null;
 1298  
         }
 1299  0
         if (libraryVersion != null && libraryVersion.length() == 0)
 1300  
         {
 1301  0
             libraryVersion = null;
 1302  
         }
 1303  0
         if (resourceName != null && resourceName.length() == 0)
 1304  
         {
 1305  0
             resourceName = null;
 1306  
         }
 1307  0
         if (resourceVersion != null && resourceVersion.length() == 0)
 1308  
         {
 1309  0
             resourceVersion = null;
 1310  
         }
 1311  
 
 1312  0
         resourceMeta = resourceLoader.createResourceMeta(
 1313  
             localePrefix, libraryName, libraryVersion, resourceName, resourceVersion);
 1314  
 
 1315  0
         if (resourceMeta != null &&
 1316  
             !resourceLoader.resourceExists(resourceMeta))
 1317  
         {
 1318  0
             resourceMeta = null;
 1319  
         }
 1320  
         //}
 1321  0
         return resourceMeta;
 1322  
     }
 1323  
     
 1324  
     protected ResourceMeta deriveResourceMeta(FacesContext context, ContractResourceLoader resourceLoader,
 1325  
             String resourceId, String contractName)
 1326  
     {
 1327  0
         ResourceMeta resourceMeta = null;
 1328  0
         String token = null;
 1329  0
         String localePrefix = null;
 1330  0
         String libraryName = null;
 1331  0
         String libraryVersion = null;
 1332  0
         String resourceName = null;
 1333  0
         String resourceVersion = null;
 1334  
         
 1335  
         // Check if resource exists. It avoids additional 
 1336  
         // checks and it can be done very quickly because the 
 1337  
         // loader always uses the resourceId structure to
 1338  
         // organize resources. But decompose the resourceId is
 1339  
         // even faster.
 1340  
         //if (resourceLoader.resourceIdExists(resourceId))
 1341  
         //{
 1342  0
         int lastSlash = resourceId.lastIndexOf('/');
 1343  0
         if (lastSlash < 0)
 1344  
         {
 1345  
             //no slashes, so it is just a plain resource.
 1346  0
             resourceName = resourceId;
 1347  
         }
 1348  
         else
 1349  
         {
 1350  0
             token = resourceId.substring(lastSlash+1);
 1351  0
             if (RESOURCE_VERSION_CHECKER.matcher(token).matches())
 1352  
             {
 1353  0
                 int secondLastSlash = resourceId.lastIndexOf('/', lastSlash-1);
 1354  0
                 if (secondLastSlash < 0)
 1355  
                 {
 1356  0
                     secondLastSlash = 0;
 1357  
                 }
 1358  
 
 1359  0
                 String rnToken = resourceId.substring(secondLastSlash+1, lastSlash);
 1360  0
                 int lastPoint = rnToken.lastIndexOf('.');
 1361  
                 // lastPoint < 0 means it does not match, the token is not a resource version
 1362  0
                 if (lastPoint >= 0)
 1363  
                 {
 1364  0
                     String ext = rnToken.substring(lastPoint);
 1365  0
                     if (token.endsWith(ext))
 1366  
                     {
 1367  
                         //It match a versioned resource
 1368  0
                         resourceVersion = token.substring(0,token.length()-ext.length());
 1369  
                     }
 1370  
                 }
 1371  
             }
 1372  
 
 1373  
             // 1. Extract the library path and locale prefix if necessary
 1374  0
             int start = 0;
 1375  0
             int firstSlash = resourceId.indexOf('/');
 1376  
 
 1377  
             // At least one slash, check if the start is locale prefix.
 1378  0
             String bundleName = context.getApplication().getMessageBundle();
 1379  
             //If no bundle set, it can't be localePrefix
 1380  0
             if (null != bundleName)
 1381  
             {
 1382  0
                 token = resourceId.substring(start, firstSlash);
 1383  
                 //Try to derive a locale object
 1384  0
                 Locale locale = _LocaleUtils.deriveLocale(token);
 1385  
 
 1386  
                 // If the locale was derived and it is available, 
 1387  
                 // assume that portion of the resourceId it as a locale prefix.
 1388  0
                 if (locale != null && _LocaleUtils.isAvailableLocale(locale))
 1389  
                 {
 1390  0
                     localePrefix = token;
 1391  0
                     start = firstSlash+1;
 1392  
                 }
 1393  
             }
 1394  
 
 1395  
             //Check slash again from start
 1396  0
             firstSlash = resourceId.indexOf('/', start);
 1397  0
             if (firstSlash < 0)
 1398  
             {
 1399  
                 //no slashes.
 1400  0
                 resourceName = resourceId.substring(start);
 1401  
             }
 1402  
             else
 1403  
             {
 1404  
                 //check libraryName
 1405  0
                 token = resourceId.substring(start, firstSlash);
 1406  0
                 int minResourceNameSlash = (resourceVersion != null) ?
 1407  
                     resourceId.lastIndexOf('/', lastSlash-1) : lastSlash;
 1408  
                 //if (resourceLoader.libraryExists(token))
 1409  0
                 if (start < minResourceNameSlash)
 1410  
                 {
 1411  0
                     libraryName = token;
 1412  0
                     start = firstSlash+1;
 1413  
 
 1414  
                     //Now that libraryName exists, check libraryVersion
 1415  0
                     firstSlash = resourceId.indexOf('/', start);
 1416  0
                     if (firstSlash >= 0)
 1417  
                     {
 1418  0
                         token = resourceId.substring(start, firstSlash);
 1419  0
                         if (LIBRARY_VERSION_CHECKER.matcher(token).matches())
 1420  
                         {
 1421  0
                             libraryVersion = token;
 1422  0
                             start = firstSlash+1;
 1423  
                         }
 1424  
                     }
 1425  
                 }
 1426  
 
 1427  0
                 firstSlash = resourceId.indexOf('/', start);
 1428  0
                 if (firstSlash < 0)
 1429  
                 {
 1430  
                     //no slashes.
 1431  0
                     resourceName = resourceId.substring(start);
 1432  
                 }
 1433  
                 else
 1434  
                 {
 1435  
                     // Check resource version. 
 1436  0
                     if (resourceVersion != null)
 1437  
                     {
 1438  0
                         resourceName = resourceId.substring(start,lastSlash);
 1439  
                     }
 1440  
                     else
 1441  
                     {
 1442  
                         //no resource version, assume the remaining to be resource name
 1443  0
                         resourceName = resourceId.substring(start);
 1444  
                     }
 1445  
                 }
 1446  
             }
 1447  
         }
 1448  
 
 1449  
         //Check libraryName and resourceName
 1450  0
         if (resourceName == null)
 1451  
         {
 1452  0
             return null;
 1453  
         }
 1454  0
         if (!ResourceValidationUtils.isValidResourceName(resourceName))
 1455  
         {
 1456  0
             return null;
 1457  
         }
 1458  
 
 1459  0
         if (libraryName != null && !ResourceValidationUtils.isValidLibraryName(
 1460  
                 libraryName, isAllowSlashesLibraryName()))
 1461  
         {
 1462  0
             return null;
 1463  
         }
 1464  
 
 1465  
         // If some variable is "" set it as null.
 1466  0
         if (localePrefix != null && localePrefix.length() == 0)
 1467  
         {
 1468  0
             localePrefix = null;
 1469  
         }
 1470  0
         if (libraryName != null && libraryName.length() == 0)
 1471  
         {
 1472  0
             libraryName = null;
 1473  
         }
 1474  0
         if (libraryVersion != null && libraryVersion.length() == 0)
 1475  
         {
 1476  0
             libraryVersion = null;
 1477  
         }
 1478  0
         if (resourceName != null && resourceName.length() == 0)
 1479  
         {
 1480  0
             resourceName = null;
 1481  
         }
 1482  0
         if (resourceVersion != null && resourceVersion.length() == 0)
 1483  
         {
 1484  0
             resourceVersion = null;
 1485  
         }
 1486  
 
 1487  0
         resourceMeta = resourceLoader.createResourceMeta(
 1488  
             localePrefix, libraryName, libraryVersion, resourceName, resourceVersion, contractName);
 1489  
 
 1490  0
         if (resourceMeta != null &&
 1491  
             !resourceLoader.resourceExists(resourceMeta))
 1492  
         {
 1493  0
             resourceMeta = null;
 1494  
         }
 1495  
         //}
 1496  0
         return resourceMeta;
 1497  
     }
 1498  
     
 1499  
     protected ResourceMeta deriveViewResourceMeta(FacesContext context, ResourceLoader resourceLoader,
 1500  
             String resourceName, String localePrefix)
 1501  
     {
 1502  0
         ResourceMeta resourceMeta = null;
 1503  0
         String resourceVersion = null;
 1504  
 
 1505  
         //1. Try to locate resource in a localized path
 1506  0
         if (localePrefix != null)
 1507  
         {
 1508  0
             resourceVersion = resourceLoader
 1509  
                     .getResourceVersion(localePrefix + '/'+ resourceName);
 1510  0
             if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
 1511  
             {
 1512  0
                 resourceMeta = resourceLoader.createResourceMeta(localePrefix, null, null,
 1513  
                          resourceName, resourceVersion);
 1514  
             }
 1515  
 
 1516  0
             if (resourceMeta != null && !resourceLoader.resourceExists(resourceMeta))
 1517  
             {
 1518  0
                 resourceMeta = null;
 1519  
             }            
 1520  
         }
 1521  
         
 1522  
         //2. Try to localize resource in a non localized path
 1523  0
         if (resourceMeta == null)
 1524  
         {
 1525  0
             resourceVersion = resourceLoader
 1526  
                     .getResourceVersion(resourceName);
 1527  0
             if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
 1528  
             {
 1529  0
                 resourceMeta = resourceLoader.createResourceMeta(null, null, null,
 1530  
                          resourceName, resourceVersion);
 1531  
             }
 1532  
 
 1533  0
             if (resourceMeta != null && !resourceLoader.resourceExists(resourceMeta))
 1534  
             {
 1535  0
                 resourceMeta = null;
 1536  
             }            
 1537  
         }
 1538  
 
 1539  0
         return resourceMeta;        
 1540  
     }
 1541  
     
 1542  
     protected ResourceMeta deriveViewResourceMeta(FacesContext context, ContractResourceLoader resourceLoader,
 1543  
             String resourceName, String localePrefix, String contractName)
 1544  
     {
 1545  0
         ResourceMeta resourceMeta = null;
 1546  0
         String resourceVersion = null;
 1547  
 
 1548  
         //1. Try to locate resource in a localized path
 1549  0
         if (localePrefix != null)
 1550  
         {
 1551  0
             resourceVersion = resourceLoader
 1552  
                     .getResourceVersion(localePrefix + '/'+ resourceName, contractName);
 1553  0
             if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
 1554  
             {
 1555  0
                 resourceMeta = resourceLoader.createResourceMeta(localePrefix, null, null,
 1556  
                      resourceName, resourceVersion, contractName);
 1557  
             }
 1558  
 
 1559  0
             if (resourceMeta != null && !resourceLoader.resourceExists(resourceMeta))
 1560  
             {
 1561  0
                 resourceMeta = null;
 1562  
             }            
 1563  
         }
 1564  
         
 1565  
         //2. Try to localize resource in a non localized path
 1566  0
         if (resourceMeta == null)
 1567  
         {
 1568  0
             resourceVersion = resourceLoader
 1569  
                     .getResourceVersion(resourceName, contractName);
 1570  0
             if (!(resourceVersion != null && ResourceLoader.VERSION_INVALID.equals(resourceVersion)))
 1571  
             {
 1572  0
                 resourceMeta = resourceLoader.createResourceMeta(null, null, null,
 1573  
                          resourceName, resourceVersion, contractName);
 1574  
             }
 1575  
 
 1576  0
             if (resourceMeta != null && !resourceLoader.resourceExists(resourceMeta))
 1577  
             {
 1578  0
                 resourceMeta = null;
 1579  
             }            
 1580  
         }
 1581  
 
 1582  0
         return resourceMeta;
 1583  
     }
 1584  
 
 1585  
     @Override
 1586  
     public Resource createViewResource(FacesContext facesContext, String resourceName)
 1587  
     {
 1588  
         // There are some special points to remember for a view resource in comparison
 1589  
         // with a normal resource:
 1590  
         //
 1591  
         // - A view resource never has an associated library name 
 1592  
         //   (this was done to keep simplicity).
 1593  
         // - A view resource can be inside a resource library contract.
 1594  
         // - A view resource could be internationalized in the same way a normal resource.
 1595  
         // - A view resource can be created from the webapp root folder, 
 1596  
         //   a normal resource cannot.
 1597  
         // - A view resource cannot be created from /resources or META-INF/resources.
 1598  
         // 
 1599  
         // For example, a valid resourceId for a view resource is like this:
 1600  
         //
 1601  
         // [localePrefix/]resourceName[/resourceVersion]
 1602  
         //
 1603  
         // but the resource loader can ignore localePrefix or resourceVersion, like
 1604  
         // for example the webapp root folder.
 1605  
         // 
 1606  
         // When createViewResource() is called, the view must be used to derive
 1607  
         // the localePrefix and facesContext must be used to get the available contracts.
 1608  
         
 1609  0
         Resource resource = null;
 1610  
 
 1611  0
         if (resourceName == null)
 1612  
         {
 1613  0
             throw new NullPointerException();
 1614  
         }
 1615  0
         if (resourceName.charAt(0) == '/')
 1616  
         {
 1617  
             // If resourceName starts with '/', remove that character because it
 1618  
             // does not have any meaning (with and without should point to the 
 1619  
             // same resource).
 1620  0
             resourceName = resourceName.substring(1);
 1621  
         }
 1622  
         
 1623  
         // Later in deriveResourceMeta the resourceId is decomposed and
 1624  
         // its elements validated properly.
 1625  0
         if (!ResourceValidationUtils.isValidViewResource(resourceName))
 1626  
         {
 1627  0
             return null;
 1628  
         }
 1629  0
         final String localePrefix = getLocalePrefixForLocateResource(facesContext);
 1630  0
         String contentType = facesContext.getExternalContext().getMimeType(resourceName);
 1631  0
         final List<String> contracts = facesContext.getResourceLibraryContracts(); 
 1632  0
         String contractPreferred = getContractNameForLocateResource(facesContext);
 1633  0
         ResourceValue resourceValue = null;
 1634  
         
 1635  
         // Check cache:
 1636  
         //
 1637  
         // Contracts are on top of everything, because it is a concept that defines
 1638  
         // resources in a application scope concept. It means all resources in
 1639  
         // /resources or /META-INF/resources can be overriden using a contract. Note
 1640  
         // it also means resources under /META-INF/flows can also be overriden using
 1641  
         // a contract.
 1642  0
         if (contractPreferred != null)
 1643  
         {
 1644  0
             resourceValue = getResourceLoaderCache().getViewResource(
 1645  
                     resourceName, contentType, localePrefix, contractPreferred);
 1646  
         }
 1647  0
         if (resourceValue == null && !contracts.isEmpty())
 1648  
         {
 1649  
             // Try to get resource but try with a contract name
 1650  0
             for (String contract : contracts)
 1651  
             {
 1652  0
                 resourceValue = getResourceLoaderCache().getViewResource(
 1653  
                     resourceName, contentType, localePrefix, contract);
 1654  0
                 if (resourceValue != null)
 1655  
                 {
 1656  0
                     break;
 1657  
                 }
 1658  0
             }
 1659  
         }
 1660  0
         if (resourceValue == null)
 1661  
         {
 1662  
             // Try to get resource without contract name
 1663  0
             resourceValue = getResourceLoaderCache().getViewResource(
 1664  
                 resourceName, contentType, localePrefix);
 1665  
         }
 1666  
 
 1667  0
         if(resourceValue != null)
 1668  
         {        
 1669  0
             resource = new ResourceImpl(resourceValue.getResourceMeta(), resourceValue.getResourceLoader(),
 1670  
                     getResourceHandlerSupport(), contentType, 
 1671  
                     resourceValue.getCachedInfo() != null ? resourceValue.getCachedInfo().getURL() : null, null);
 1672  
         }
 1673  
         else
 1674  
         {
 1675  0
             boolean resolved = false;
 1676  0
             if (contractPreferred != null)
 1677  
             {
 1678  0
                 for (ContractResourceLoader loader : getResourceHandlerSupport().getContractResourceLoaders())
 1679  
                 {
 1680  0
                     ResourceMeta resourceMeta = deriveViewResourceMeta(
 1681  
                         facesContext, loader, resourceName, localePrefix, contractPreferred);
 1682  0
                     if (resourceMeta != null)
 1683  
                     {
 1684  0
                         resource = new ResourceImpl(resourceMeta, loader, 
 1685  
                             getResourceHandlerSupport(), contentType);
 1686  
 
 1687  
                         // cache it
 1688  0
                         getResourceLoaderCache().putViewResource(
 1689  
                             resourceName, contentType, localePrefix, contractPreferred, resourceMeta, loader, 
 1690  
                             new ResourceCachedInfo(resource.getURL(), null));
 1691  
                         
 1692  0
                         resolved = true;
 1693  0
                         break;
 1694  
                     }
 1695  
                 }
 1696  
             }
 1697  0
             if (!resolved && !contracts.isEmpty())
 1698  
             {
 1699  
                 for (ContractResourceLoader loader : 
 1700  0
                         getResourceHandlerSupport().getContractResourceLoaders())
 1701  
                 {
 1702  0
                     for (String contract : contracts)
 1703  
                     {
 1704  0
                         ResourceMeta resourceMeta = deriveViewResourceMeta(
 1705  
                             facesContext, loader, resourceName, localePrefix, contract);
 1706  0
                         if (resourceMeta != null)
 1707  
                         {
 1708  0
                             resource = new ResourceImpl(resourceMeta, loader, 
 1709  
                                 getResourceHandlerSupport(), contentType);
 1710  
 
 1711  
                             // cache it
 1712  0
                             getResourceLoaderCache().putViewResource(
 1713  
                                 resourceName, contentType, localePrefix, contract, resourceMeta, loader,
 1714  
                                 new ResourceCachedInfo(resource.getURL(), null));
 1715  
 
 1716  0
                             resolved = true;
 1717  0
                             break;
 1718  
                         }
 1719  0
                     }
 1720  
                 }
 1721  
             }
 1722  0
             if (!resolved)
 1723  
             {
 1724  
                 // "... Considering the web app root ..."
 1725  
                 
 1726  
                 // "... Considering faces flows (at the locations specified in the spec prose document section 
 1727  
                 // Faces Flows in the Using JSF in Web Applications chapter) ..."
 1728  0
                 for (ResourceLoader loader : getResourceHandlerSupport().getViewResourceLoaders())
 1729  
                 {
 1730  0
                     ResourceMeta resourceMeta = deriveViewResourceMeta(
 1731  
                         facesContext, loader, resourceName, localePrefix);
 1732  
 
 1733  0
                     if (resourceMeta != null)
 1734  
                     {
 1735  0
                         resource = new ResourceImpl(resourceMeta, loader, getResourceHandlerSupport(), contentType);
 1736  
 
 1737  
                         // cache it
 1738  0
                         getResourceLoaderCache().putViewResource(
 1739  
                             resourceName, contentType, localePrefix, resourceMeta, loader,
 1740  
                             new ResourceCachedInfo(resource.getURL(), null));
 1741  0
                         break;
 1742  
                     }
 1743  
                 }
 1744  
             }
 1745  
         }
 1746  0
         return resource;
 1747  
     }
 1748  
 
 1749  
 }