Coverage Report - org.apache.myfaces.resource.TempDirFileCacheContractResourceLoader
 
Classes in this File Line Coverage Branch Coverage Complexity
TempDirFileCacheContractResourceLoader
0%
0/80
0%
0/26
2.625
TempDirFileCacheContractResourceLoader$FileProducer
0%
0/8
0%
0/2
2.625
 
 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.shared.resource.ContractResourceLoader;
 38  
 import org.apache.myfaces.shared.resource.ContractResourceLoaderWrapper;
 39  
 import org.apache.myfaces.shared.resource.ResourceMeta;
 40  
 import org.apache.myfaces.shared.util.WebConfigParamUtils;
 41  
 
 42  
 /**
 43  
  * ResourceLoader that uses a temporal folder to cache resources, avoiding the problem
 44  
  * described on  MYFACES-3586 (Performance improvement in Resource loading - 
 45  
  * HIGH CPU inflating bytes in ResourceHandlerImpl.handleResourceRequest).
 46  
  *
 47  
  * @author Leonardo Uribe
 48  
  */
 49  0
 public class TempDirFileCacheContractResourceLoader extends ContractResourceLoaderWrapper
 50  
 {
 51  
     
 52  
     public final static String TEMP_FILES_LOCK_MAP = "oam.rh.con.TEMP_FILES_LOCK_MAP";
 53  
     
 54  
     /**
 55  
      * Subdir of the ServletContext tmp dir to store temporal resources.
 56  
      */
 57  
     private static final String TEMP_FOLDER_BASE_DIR = "oam-rh-cache/";
 58  
 
 59  
     /**
 60  
      * Suffix for temporal files.
 61  
      */
 62  
     private static final String TEMP_FILE_SUFFIX = ".tmp";
 63  
     
 64  
     private final ContractResourceLoader delegate;
 65  
     
 66  
     private volatile File _tempDir;
 67  
     
 68  0
     private int _resourceBufferSize = -1;
 69  
     
 70  
     public TempDirFileCacheContractResourceLoader(ContractResourceLoader delegate)
 71  0
     {
 72  0
         this.delegate = delegate;
 73  0
         initialize();
 74  0
     }
 75  
     
 76  
     protected void initialize()
 77  
     {
 78  
         //Get startup FacesContext
 79  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 80  
     
 81  
         //1. Create temporal directory for temporal resources
 82  0
         Map<String, Object> applicationMap = facesContext.getExternalContext().getApplicationMap();
 83  0
         File tempdir = (File) applicationMap.get("javax.servlet.context.tempdir");
 84  0
         File imagesDir = new File(tempdir, TEMP_FOLDER_BASE_DIR);
 85  0
         if (!imagesDir.exists())
 86  
         {
 87  0
             imagesDir.mkdirs();
 88  
         }
 89  
         else
 90  
         {
 91  
             //Clear the cache
 92  0
             deleteDir(imagesDir);
 93  0
             imagesDir.mkdirs();
 94  
         }
 95  0
         _tempDir = imagesDir;
 96  
 
 97  
         //2. Create map for register temporal resources
 98  0
         Map<String, FileProducer> temporalFilesLockMap = new ConcurrentHashMap<String, FileProducer>();
 99  0
         facesContext.getExternalContext().getApplicationMap().put(TEMP_FILES_LOCK_MAP, temporalFilesLockMap);
 100  0
     }
 101  
 
 102  
     private static boolean deleteDir(File dir)
 103  
     {
 104  0
         if (dir.isDirectory())
 105  
         {
 106  0
             String[] children = dir.list();
 107  0
             for (int i = 0; i < children.length; i++)
 108  
             {
 109  0
                 boolean success = deleteDir(new File(dir, children[i]));
 110  0
                 if (!success)
 111  
                 {
 112  0
                     return false;
 113  
                 }
 114  
             }
 115  
         }
 116  0
         return dir.delete();
 117  
     }
 118  
     
 119  
     @Override
 120  
     public URL getResourceURL(ResourceMeta resourceMeta)
 121  
     {
 122  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 123  
 
 124  0
         if (resourceExists(resourceMeta))
 125  
         {
 126  0
             File file = createOrGetTempFile(facesContext, resourceMeta);
 127  
             
 128  
             try
 129  
             {
 130  0
                 return file.toURL();
 131  
             }
 132  0
             catch (MalformedURLException e)
 133  
             {
 134  0
                 throw new FacesException(e);
 135  
             }
 136  
         }
 137  
         else
 138  
         {
 139  0
             return null;
 140  
         }
 141  
     }    
 142  
     
 143  
     public InputStream getResourceInputStream(ResourceMeta resourceMeta, Resource resource)
 144  
     {
 145  0
         FacesContext facesContext = FacesContext.getCurrentInstance();
 146  
 
 147  0
         if (resourceExists(resourceMeta))
 148  
         {
 149  0
             File file = createOrGetTempFile(facesContext, resourceMeta);
 150  
             
 151  
             try
 152  
             {
 153  0
                 return new BufferedInputStream(new FileInputStream(file));
 154  
             }
 155  0
             catch (FileNotFoundException e)
 156  
             {
 157  0
                 throw new FacesException(e);
 158  
             }
 159  
         }
 160  
         else
 161  
         {
 162  0
             return null;
 163  
         }
 164  
     }
 165  
 
 166  
     @Override
 167  
     public InputStream getResourceInputStream(ResourceMeta resourceMeta)
 168  
     {
 169  0
         return getResourceInputStream(resourceMeta, null);
 170  
     }
 171  
     
 172  
     @Override
 173  
     public boolean resourceExists(ResourceMeta resourceMeta)
 174  
     {
 175  0
         return super.resourceExists(resourceMeta);
 176  
     }
 177  
 
 178  
     @SuppressWarnings("unchecked")
 179  
     private File createOrGetTempFile(FacesContext facesContext, ResourceMeta resourceMeta)
 180  
     {
 181  0
         String identifier = resourceMeta.getResourceIdentifier()+"_"+resourceMeta.getContractName();
 182  0
         File file = getTemporalFile(resourceMeta);
 183  0
         if (!file.exists())
 184  
         {
 185  0
             Map<String, FileProducer> map = (Map<String, FileProducer>) 
 186  
                 facesContext.getExternalContext().getApplicationMap().get(TEMP_FILES_LOCK_MAP);
 187  
 
 188  0
             FileProducer creator = map.get(identifier);
 189  
             
 190  0
             if (creator == null)
 191  
             {
 192  0
                 synchronized(this)
 193  
                 {
 194  0
                     creator = map.get(identifier);
 195  
                     
 196  0
                     if (creator == null)
 197  
                     {
 198  0
                         creator = new FileProducer();
 199  0
                         map.put(identifier, creator);
 200  
                     }
 201  0
                 }
 202  
             }
 203  
             
 204  0
             if (!creator.isCreated())
 205  
             {
 206  0
                 creator.createFile(facesContext, resourceMeta, file, this);
 207  
             }
 208  
         }
 209  0
         return file;
 210  
     }    
 211  
     
 212  
     private File getTemporalFile(ResourceMeta resourceMeta)
 213  
     {
 214  0
         return new File(_tempDir, 
 215  
             resourceMeta.getResourceIdentifier()+"_"+ resourceMeta.getContractName() + TEMP_FILE_SUFFIX);
 216  
     }
 217  
 
 218  
     protected void createTemporalFileVersion(FacesContext facesContext, ResourceMeta resourceMeta, File target)
 219  
     {
 220  0
         target.mkdirs();  // ensure necessary directories exist
 221  0
         target.delete();  // remove any existing file
 222  
 
 223  0
         InputStream inputStream = null;
 224  
         FileOutputStream fileOutputStream;
 225  
         try
 226  
         {
 227  0
             inputStream = getWrapped().getResourceInputStream(resourceMeta);
 228  0
             fileOutputStream = new FileOutputStream(target);
 229  0
             byte[] buffer = new byte[this.getResourceBufferSize()];
 230  
 
 231  0
             pipeBytes(inputStream, fileOutputStream, buffer);
 232  
         }
 233  0
         catch (FileNotFoundException e)
 234  
         {
 235  0
             throw new FacesException("Unexpected exception while create file:", e);
 236  
         }
 237  0
         catch (IOException e)
 238  
         {
 239  0
             throw new FacesException("Unexpected exception while create file:", e);
 240  
         }
 241  
         finally
 242  
         {
 243  0
             if (inputStream != null)
 244  
             {
 245  
                 try
 246  
                 {
 247  0
                     inputStream.close();
 248  
                 }
 249  0
                 catch (IOException e)
 250  
                 {
 251  
                     // Ignore
 252  0
                 }
 253  
             }
 254  
         }
 255  0
     }
 256  
     
 257  
     /**
 258  
      * Reads the specified input stream into the provided byte array storage and
 259  
      * writes it to the output stream.
 260  
      */
 261  
     private static void pipeBytes(InputStream in, OutputStream out, byte[] buffer) throws IOException
 262  
     {
 263  
         int length;
 264  
 
 265  0
         while ((length = (in.read(buffer))) >= 0)
 266  
         {
 267  0
             out.write(buffer, 0, length);
 268  
         }
 269  0
     }
 270  
     
 271  
     public static class FileProducer 
 272  
     {
 273  
         
 274  0
         public volatile boolean created = false;
 275  
         
 276  
         public FileProducer()
 277  
         {
 278  0
             super();
 279  0
         }
 280  
 
 281  
         public boolean isCreated()
 282  
         {
 283  0
             return created;
 284  
         }
 285  
 
 286  
         public synchronized void createFile(FacesContext facesContext, 
 287  
             ResourceMeta resourceMeta, File file, TempDirFileCacheContractResourceLoader loader)
 288  
         {
 289  0
             if (!created)
 290  
             {
 291  0
                 loader.createTemporalFileVersion(facesContext, resourceMeta, file);
 292  0
                 created = true;
 293  
             }
 294  0
         }
 295  
     }
 296  
     
 297  
     protected int getResourceBufferSize()
 298  
     {
 299  0
         if (_resourceBufferSize == -1)
 300  
         {
 301  0
             _resourceBufferSize = WebConfigParamUtils.getIntegerInitParameter(
 302  
                 FacesContext.getCurrentInstance().getExternalContext(),
 303  
                 ResourceHandlerImpl.INIT_PARAM_RESOURCE_BUFFER_SIZE,
 304  
                 ResourceHandlerImpl.INIT_PARAM_RESOURCE_BUFFER_SIZE_DEFAULT);
 305  
         }
 306  0
         return _resourceBufferSize;
 307  
     }
 308  
     
 309  
     public ContractResourceLoader getWrapped()
 310  
     {
 311  0
         return delegate;
 312  
     }
 313  
 }