Coverage Report - org.apache.myfaces.resource.TempDirFileCacheResourceLoader
 
Classes in this File Line Coverage Branch Coverage Complexity
TempDirFileCacheResourceLoader
0%
0/90
0%
0/30
2.824
TempDirFileCacheResourceLoader$FileProducer
0%
0/8
0%
0/2
2.824
 
 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.resource;
 20  
 
 21  
 import java.io.BufferedInputStream;
 22  
 import java.io.File;
 23  
 import java.io.FileInputStream;
 24  
 import java.io.FileNotFoundException;
 25  
 import java.io.FileOutputStream;
 26  
 import java.io.IOException;
 27  
 import java.io.InputStream;
 28  
 import java.io.OutputStream;
 29  
 import java.net.MalformedURLException;
 30  
 import java.net.URL;
 31  
 import java.util.Map;
 32  
 import java.util.concurrent.ConcurrentHashMap;
 33  
 import javax.faces.FacesException;
 34  
 import javax.faces.application.Resource;
 35  
 import javax.faces.context.FacesContext;
 36  
 import org.apache.myfaces.application.ResourceHandlerImpl;
 37  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
 38  
 import org.apache.myfaces.shared.resource.ResourceLoader;
 39  
 import org.apache.myfaces.shared.resource.ResourceLoaderWrapper;
 40  
 import org.apache.myfaces.shared.resource.ResourceMeta;
 41  
 import org.apache.myfaces.shared.util.WebConfigParamUtils;
 42  
 
 43  
 /**
 44  
  * ResourceLoader that uses a temporal folder to cache resources, avoiding the problem
 45  
  * described on  MYFACES-3586 (Performance improvement in Resource loading - 
 46  
  * HIGH CPU inflating bytes in ResourceHandlerImpl.handleResourceRequest).
 47  
  *
 48  
  * @author Leonardo Uribe
 49  
  */
 50  0
 public class TempDirFileCacheResourceLoader extends ResourceLoaderWrapper
 51  
 {
 52  
     /**
 53  
      * If this param is set to true (default false), a temporal directory is created and
 54  
      * all files handled by this ResourceLoader are cached there, avoiding the problem
 55  
      * described on MYFACES-3586. (Performance improvement in Resource loading - 
 56  
      * HIGH CPU inflating bytes in ResourceHandlerImpl.handleResourceRequest).
 57  
      */
 58  
     @JSFWebConfigParam(since="2.1.11", expectedValues="true, false", defaultValue="false")
 59  
     public final static String INIT_PARAM_TEMPORAL_RESOURCEHANDLER_CACHE_ENABLED = 
 60  
         "org.apache.myfaces.TEMPORAL_RESOURCEHANDLER_CACHE_ENABLED";
 61  
     public final static boolean INIT_PARAM_TEMPORAL_RESOURCEHANDLER_CACHE_ENABLED_DEFAULT = false;
 62  
     
 63  
     public final static String TEMP_FILES_LOCK_MAP = "oam.rh.TEMP_FILES_LOCK_MAP";
 64  
     
 65  
     /**
 66  
      * Subdir of the ServletContext tmp dir to store temporal resources.
 67  
      */
 68  
     private static final String TEMP_FOLDER_BASE_DIR = "oam-rh-cache/";
 69  
 
 70  
     /**
 71  
      * Suffix for temporal files.
 72  
      */
 73  
     private static final String TEMP_FILE_SUFFIX = ".tmp";
 74  
     
 75  
     private ResourceLoader delegate;
 76  
     
 77  
     private volatile File _tempDir;
 78  
     
 79  0
     private int _resourceBufferSize = -1;
 80  
     
 81  
     public TempDirFileCacheResourceLoader(ResourceLoader delegate)
 82  0
     {
 83  0
         this.delegate = delegate;
 84  0
         initialize();
 85  0
     }
 86  
     
 87  
     public static boolean isValidCreateTemporalFiles(FacesContext facesContext)
 88  
     {
 89  0
         if (WebConfigParamUtils.getBooleanInitParameter(facesContext.getExternalContext(),
 90  
             INIT_PARAM_TEMPORAL_RESOURCEHANDLER_CACHE_ENABLED,
 91  
             INIT_PARAM_TEMPORAL_RESOURCEHANDLER_CACHE_ENABLED_DEFAULT))
 92  
         {
 93  
             // Try create a temporal folder to check if is valid to do so, otherwise, disable it.
 94  
             try
 95  
             {
 96  0
                 Map<String, Object> applicationMap = facesContext.getExternalContext().getApplicationMap();
 97  0
                 File tempdir = (File) applicationMap.get("javax.servlet.context.tempdir");
 98  0
                 File imagesDir = new File(tempdir, TEMP_FOLDER_BASE_DIR);
 99  0
                 if (!imagesDir.exists())
 100  
                 {
 101  0
                     imagesDir.mkdirs();
 102  
                 }
 103  0
                 return true;
 104  
             }
 105  0
             catch (Exception e)
 106  
             {
 107  0
                 return false;
 108  
             }
 109  
         }
 110  
         else
 111  
         {
 112  0
             return false;
 113  
         }
 114  
     }
 115  
     
 116  
     protected void initialize()
 117  
     {
 118  
         //Get startup FacesContext
 119  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 120  
     
 121  
         //1. Create temporal directory for temporal resources
 122  0
         Map<String, Object> applicationMap = facesContext.getExternalContext().getApplicationMap();
 123  0
         File tempdir = (File) applicationMap.get("javax.servlet.context.tempdir");
 124  0
         File imagesDir = new File(tempdir, TEMP_FOLDER_BASE_DIR);
 125  0
         if (!imagesDir.exists())
 126  
         {
 127  0
             imagesDir.mkdirs();
 128  
         }
 129  
         else
 130  
         {
 131  
             //Clear the cache
 132  0
             deleteDir(imagesDir);
 133  0
             imagesDir.mkdirs();
 134  
         }
 135  0
         _tempDir = imagesDir;
 136  
 
 137  
         //2. Create map for register temporal resources
 138  0
         Map<String, FileProducer> temporalFilesLockMap = new ConcurrentHashMap<String, FileProducer>();
 139  0
         facesContext.getExternalContext().getApplicationMap().put(TEMP_FILES_LOCK_MAP, temporalFilesLockMap);
 140  0
     }
 141  
 
 142  
     private static boolean deleteDir(File dir)
 143  
     {
 144  0
         if (dir.isDirectory())
 145  
         {
 146  0
             String[] children = dir.list();
 147  0
             for (int i = 0; i < children.length; i++)
 148  
             {
 149  0
                 boolean success = deleteDir(new File(dir, children[i]));
 150  0
                 if (!success)
 151  
                 {
 152  0
                     return false;
 153  
                 }
 154  
             }
 155  
         }
 156  0
         return dir.delete();
 157  
     }
 158  
     
 159  
     @Override
 160  
     public URL getResourceURL(ResourceMeta resourceMeta)
 161  
     {
 162  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 163  
 
 164  0
         if (resourceExists(resourceMeta))
 165  
         {
 166  0
             File file = createOrGetTempFile(facesContext, resourceMeta);
 167  
             
 168  
             try
 169  
             {
 170  0
                 return file.toURL();
 171  
             }
 172  0
             catch (MalformedURLException e)
 173  
             {
 174  0
                 throw new FacesException(e);
 175  
             }
 176  
         }
 177  
         else
 178  
         {
 179  0
             return null;
 180  
         }
 181  
     }    
 182  
     
 183  
     public InputStream getResourceInputStream(ResourceMeta resourceMeta, Resource resource)
 184  
     {
 185  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 186  
 
 187  0
         if (resourceExists(resourceMeta))
 188  
         {
 189  0
             File file = createOrGetTempFile(facesContext, resourceMeta);
 190  
             
 191  
             try
 192  
             {
 193  0
                 return new BufferedInputStream(new FileInputStream(file));
 194  
             }
 195  0
             catch (FileNotFoundException e)
 196  
             {
 197  0
                 throw new FacesException(e);
 198  
             }
 199  
         }
 200  
         else
 201  
         {
 202  0
             return null;
 203  
         }
 204  
     }
 205  
 
 206  
     @Override
 207  
     public InputStream getResourceInputStream(ResourceMeta resourceMeta)
 208  
     {
 209  0
         return getResourceInputStream(resourceMeta, null);
 210  
     }
 211  
     
 212  
     @Override
 213  
     public boolean resourceExists(ResourceMeta resourceMeta)
 214  
     {
 215  0
         return super.resourceExists(resourceMeta);
 216  
     }
 217  
 
 218  
     @SuppressWarnings("unchecked")
 219  
     private File createOrGetTempFile(FacesContext facesContext, ResourceMeta resourceMeta)
 220  
     {
 221  0
         String identifier = resourceMeta.getResourceIdentifier();
 222  0
         File file = getTemporalFile(resourceMeta);
 223  0
         if (!file.exists())
 224  
         {
 225  0
             Map<String, FileProducer> map = (Map<String, FileProducer>) 
 226  
                 facesContext.getExternalContext().getApplicationMap().get(TEMP_FILES_LOCK_MAP);
 227  
 
 228  0
             FileProducer creator = map.get(identifier);
 229  
             
 230  0
             if (creator == null)
 231  
             {
 232  0
                 synchronized(this)
 233  
                 {
 234  0
                     creator = map.get(identifier);
 235  
                     
 236  0
                     if (creator == null)
 237  
                     {
 238  0
                         creator = new FileProducer();
 239  0
                         map.put(identifier, creator);
 240  
                     }
 241  0
                 }
 242  
             }
 243  
             
 244  0
             if (!creator.isCreated())
 245  
             {
 246  0
                 creator.createFile(facesContext, resourceMeta, file, this);
 247  
             }
 248  
         }
 249  0
         return file;
 250  
     }    
 251  
     
 252  
     private File getTemporalFile(ResourceMeta resourceMeta)
 253  
     {
 254  0
         return new File(_tempDir, resourceMeta.getResourceIdentifier() + TEMP_FILE_SUFFIX);
 255  
     }
 256  
 
 257  
     /*
 258  
     private boolean couldResourceContainValueExpressions(ResourceMeta resourceMeta)
 259  
     {
 260  
         return resourceMeta.couldResourceContainValueExpressions() || resourceMeta.getResourceName().endsWith(".css");
 261  
     }*/
 262  
     
 263  
     protected void createTemporalFileVersion(FacesContext facesContext, ResourceMeta resourceMeta, File target)
 264  
     {
 265  0
         target.mkdirs();  // ensure necessary directories exist
 266  0
         target.delete();  // remove any existing file
 267  
 
 268  0
         InputStream inputStream = null;
 269  
         FileOutputStream fileOutputStream;
 270  
         try
 271  
         {
 272  
             /*
 273  
             if (couldResourceContainValueExpressions(resourceMeta))
 274  
             {
 275  
                 inputStream = new ValueExpressionFilterInputStream(
 276  
                         getWrapped().getResourceInputStream(resourceMeta),
 277  
                         resourceMeta.getLibraryName(), 
 278  
                         resourceMeta.getResourceName());
 279  
             }
 280  
             else
 281  
             {*/
 282  0
                 inputStream = getWrapped().getResourceInputStream(resourceMeta);
 283  
             /*}*/
 284  0
             fileOutputStream = new FileOutputStream(target);
 285  0
             byte[] buffer = new byte[this.getResourceBufferSize()];
 286  
 
 287  0
             pipeBytes(inputStream, fileOutputStream, buffer);
 288  
         }
 289  0
         catch (FileNotFoundException e)
 290  
         {
 291  0
             throw new FacesException("Unexpected exception while create file:", e);
 292  
         }
 293  0
         catch (IOException e)
 294  
         {
 295  0
             throw new FacesException("Unexpected exception while create file:", e);
 296  
         }
 297  
         finally
 298  
         {
 299  0
             if (inputStream != null)
 300  
             {
 301  
                 try
 302  
                 {
 303  0
                     inputStream.close();
 304  
                 }
 305  0
                 catch (IOException e)
 306  
                 {
 307  
                     // Ignore
 308  0
                 }
 309  
             }
 310  
         }
 311  0
     }
 312  
     
 313  
     /**
 314  
      * Reads the specified input stream into the provided byte array storage and
 315  
      * writes it to the output stream.
 316  
      */
 317  
     private static void pipeBytes(InputStream in, OutputStream out, byte[] buffer) throws IOException
 318  
     {
 319  
         int length;
 320  
 
 321  0
         while ((length = (in.read(buffer))) >= 0)
 322  
         {
 323  0
             out.write(buffer, 0, length);
 324  
         }
 325  0
     }
 326  
     
 327  
     public static class FileProducer 
 328  
     {
 329  
         
 330  0
         public volatile boolean created = false;
 331  
         
 332  
         public FileProducer()
 333  
         {
 334  0
             super();
 335  0
         }
 336  
 
 337  
         public boolean isCreated()
 338  
         {
 339  0
             return created;
 340  
         }
 341  
 
 342  
         public synchronized void createFile(FacesContext facesContext, 
 343  
             ResourceMeta resourceMeta, File file, TempDirFileCacheResourceLoader loader)
 344  
         {
 345  0
             if (!created)
 346  
             {
 347  0
                 loader.createTemporalFileVersion(facesContext, resourceMeta, file);
 348  0
                 created = true;
 349  
             }
 350  0
         }
 351  
     }
 352  
     
 353  
     protected int getResourceBufferSize()
 354  
     {
 355  0
         if (_resourceBufferSize == -1)
 356  
         {
 357  0
             _resourceBufferSize = WebConfigParamUtils.getIntegerInitParameter(
 358  
                 FacesContext.getCurrentInstance().getExternalContext(),
 359  
                 ResourceHandlerImpl.INIT_PARAM_RESOURCE_BUFFER_SIZE,
 360  
                 ResourceHandlerImpl.INIT_PARAM_RESOURCE_BUFFER_SIZE_DEFAULT);
 361  
         }
 362  0
         return _resourceBufferSize;
 363  
     }
 364  
     
 365  
     public ResourceLoader getWrapped()
 366  
     {
 367  0
         return delegate;
 368  
     }
 369  
 }