Clover coverage report - Code Coverage for tapestry release 4.0-alpha-3
Coverage timestamp: Mon May 16 2005 09:05:49 EDT
file stats: LOC: 291   Methods: 8
NCLOC: 136   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
AssetExternalizerImpl.java 9.1% 14.3% 62.5% 17.4%
coverage coverage
 1   
 // Copyright 2004, 2005 The Apache Software Foundation
 2   
 //
 3   
 // Licensed under the Apache License, Version 2.0 (the "License");
 4   
 // you may not use this file except in compliance with the License.
 5   
 // You may obtain a copy of the License at
 6   
 //
 7   
 //     http://www.apache.org/licenses/LICENSE-2.0
 8   
 //
 9   
 // Unless required by applicable law or agreed to in writing, software
 10   
 // distributed under the License is distributed on an "AS IS" BASIS,
 11   
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12   
 // See the License for the specific language governing permissions and
 13   
 // limitations under the License.
 14   
 
 15   
 package org.apache.tapestry.asset;
 16   
 
 17   
 import java.io.BufferedInputStream;
 18   
 import java.io.BufferedOutputStream;
 19   
 import java.io.File;
 20   
 import java.io.FileOutputStream;
 21   
 import java.io.IOException;
 22   
 import java.io.InputStream;
 23   
 import java.io.OutputStream;
 24   
 import java.net.URL;
 25   
 import java.util.HashMap;
 26   
 import java.util.Map;
 27   
 
 28   
 import javax.servlet.ServletContext;
 29   
 
 30   
 import org.apache.commons.logging.Log;
 31   
 import org.apache.hivemind.ApplicationRuntimeException;
 32   
 import org.apache.hivemind.ClassResolver;
 33   
 import org.apache.tapestry.Tapestry;
 34   
 import org.apache.tapestry.engine.IPropertySource;
 35   
 import org.apache.tapestry.util.StringSplitter;
 36   
 
 37   
 /**
 38   
  * Implementation of the {@link org.apache.tapestry.asset.AssetExternalizer}service interface.
 39   
  * Responsible for copying assets from the classpath to an external directory that is visible to the
 40   
  * web server. The externalizer is stored inside the {@link ServletContext}as a named attribute.
 41   
  * <p>
 42   
  * The externalizer uses the name
 43   
  * <code>org.apache.tapestry.AssetExternalizer.<i>application name</i>
 44   
  *  </code>. It configures
 45   
  * itself using two additional properties (searching in
 46   
  * {@link org.apache.tapestry.IEngine#getPropertySource()}. <table border=1>
 47   
  * <tr>
 48   
  * <th>Parameter</th>
 49   
  * <th>Description</th>
 50   
  * </tr>
 51   
  * <tr valign=top>
 52   
  * <td><code>org.apache.tapestry.asset.dir</code></td>
 53   
  * <td>The directory to which assets will be copied.</td>
 54   
  * </tr>
 55   
  * <tr valign=top>
 56   
  * <td><code>org.apache.tapestry.asset.URL</code></td>
 57   
  * <td>The corresponding URL for the asset directory.</td>
 58   
  * </tr>
 59   
  * </table>
 60   
  * <p>
 61   
  * If either of these parameters is null, then no externalization occurs. Private assets will still
 62   
  * be available, just less efficiently, as the application will be invoked via its servlet and,
 63   
  * ultimately, the {@link AssetService}will need to retrieve the asset.
 64   
  * <p>
 65   
  * Assets maintain thier directory structure when copied. For example, an asset with a resource path
 66   
  * of <code>/com/skunkworx/Banner.gif</code> would be copied to the file system as
 67   
  * <code><i>dir</i>/com/skunkworx/Banner.gif</code> and would have a URL of
 68   
  * <code><i>URL</i>/com/skunkworx/Banner.gif</code>.
 69   
  * <p>
 70   
  * The externalizer will create any directories as needed.
 71   
  * <p>
 72   
  * The externalizer will not overwrite existing files. When a new version of the application is
 73   
  * deployed with changed assets, there are two deployment stategies:
 74   
  * <ul>
 75   
  * <li>Delete the existing asset directory and allow the externalizer to recreate and repopulate
 76   
  * it.
 77   
  * <li>Change the asset directory and URL, allowing the old and new assets to exist side-by-side.
 78   
  * </ul>
 79   
  * <p>
 80   
  * When using the second approach, it is best to use a directory that has a version number in it,
 81   
  * for example, <code>D:/inetpub/assets/0</code> mapped to the URL <code>/assets/0</code>. When
 82   
  * a new version of the application is deployed, the trailing version number is incremented from 0
 83   
  * to 1.
 84   
  * 
 85   
  * @author Howard Lewis Ship
 86   
  */
 87   
 
 88   
 public class AssetExternalizerImpl implements AssetExternalizer
 89   
 {
 90   
     /** @since 4.0 */
 91   
     private Log _log;
 92   
 
 93   
     private ClassResolver _resolver;
 94   
 
 95   
     /** @since 4.0 */
 96   
     private IPropertySource _propertySource;
 97   
 
 98   
     private File _assetDir;
 99   
 
 100   
     private String _URL;
 101   
 
 102   
     /**
 103   
      * A map from resource path (as a String) to final URL (as a String).
 104   
      */
 105   
 
 106   
     private Map _resources = new HashMap();
 107   
 
 108   
     private static final int BUFFER_SIZE = 2048;
 109   
 
 110  27
     public void initializeService()
 111   
     {
 112  27
         String directory = _propertySource.getPropertyValue("org.apache.tapestry.asset.dir");
 113   
 
 114  27
         if (directory == null)
 115  27
             return;
 116   
 
 117  0
         _URL = _propertySource.getPropertyValue("org.apache.tapestry.asset.URL");
 118   
 
 119  0
         if (_URL == null)
 120  0
             return;
 121   
 
 122  0
         _assetDir = new File(directory);
 123   
 
 124  0
         _log.debug("Initialized with directory " + _assetDir + " mapped to " + _URL);
 125   
     }
 126   
 
 127  0
     protected void externalize(String resourcePath) throws IOException
 128   
     {
 129  0
         if (_log.isDebugEnabled())
 130  0
             _log.debug("Externalizing " + resourcePath);
 131   
 
 132  0
         File file = _assetDir;
 133   
 
 134   
         // Resources are always split by the unix seperator, even on Win32.
 135   
 
 136  0
         StringSplitter splitter = new StringSplitter('/');
 137   
 
 138  0
         String[] path = splitter.splitToArray(resourcePath);
 139   
 
 140   
         // The path is expected to start with a leading slash, but the StringSplitter
 141   
         // will ignore that leading slash.
 142   
 
 143  0
         for (int i = 0; i < path.length - 1; i++)
 144   
         {
 145   
             // Doing it this way makes sure the path seperators are right.
 146   
 
 147  0
             file = new File(file, path[i]);
 148   
         }
 149   
 
 150   
         // Make sure the directories exist.
 151   
 
 152  0
         file.mkdirs();
 153   
 
 154  0
         file = new File(file, path[path.length - 1]);
 155   
 
 156   
         // If the file exists, then assume all is well. This is OK for development,
 157   
         // but there may be multithreading (or even multiprocess) race conditions
 158   
         // around the creation of the file.
 159   
 
 160  0
         if (file.exists())
 161  0
             return;
 162   
 
 163   
         // Get the resource and copy it to the file.
 164   
 
 165  0
         URL inputURL = _resolver.getResource(resourcePath);
 166  0
         if (inputURL == null)
 167  0
             throw new IOException(Tapestry.format("missing-resource", resourcePath));
 168   
 
 169  0
         InputStream in = null;
 170  0
         OutputStream out = null;
 171   
 
 172  0
         try
 173   
         {
 174  0
             in = new BufferedInputStream(inputURL.openStream());
 175   
 
 176  0
             out = new BufferedOutputStream(new FileOutputStream(file));
 177   
 
 178  0
             byte[] buffer = new byte[BUFFER_SIZE];
 179   
 
 180  0
             while (true)
 181   
             {
 182  0
                 int bytesRead = in.read(buffer, 0, BUFFER_SIZE);
 183  0
                 if (bytesRead < 0)
 184  0
                     break;
 185   
 
 186  0
                 out.write(buffer, 0, bytesRead);
 187   
             }
 188   
         }
 189   
         finally
 190   
         {
 191  0
             close(in);
 192  0
             close(out);
 193   
         }
 194   
 
 195   
         // The file is copied!
 196   
     }
 197   
 
 198  0
     private void close(InputStream in)
 199   
     {
 200  0
         if (in != null)
 201  0
             try
 202   
             {
 203  0
                 in.close();
 204   
             }
 205   
             catch (IOException ex)
 206   
             {
 207   
                 // Ignore.
 208   
             }
 209   
     }
 210   
 
 211  0
     private void close(OutputStream out)
 212   
     {
 213  0
         if (out != null)
 214   
 
 215  0
             try
 216   
             {
 217  0
                 out.close();
 218   
             }
 219   
             catch (IOException ex)
 220   
             {
 221   
                 // Ignore
 222   
             }
 223   
     }
 224   
 
 225   
     /**
 226   
      * Gets the URL to a private resource. If the resource was previously copied out of the
 227   
      * classpath, the previously generated URL is returned.
 228   
      * <p>
 229   
      * If the asset directory and URL are not configured, then returns null.
 230   
      * <p>
 231   
      * Otherwise, the asset is copied out to the asset directory, the URL is constructed (and
 232   
      * recorded for later) and the URL is returned.
 233   
      * <p>
 234   
      * This method is not explicitly synchronized but should work multi-threaded. It synchronizes on
 235   
      * the internal <code>Map</code> used to map resource paths to URLs.
 236   
      * 
 237   
      * @param resourcePath
 238   
      *            The full path of the resource within the classpath. This is expected to include a
 239   
      *            leading slash. For example: <code>/com/skunkworx/Banner.gif</code>.
 240   
      */
 241   
 
 242  58
     public String getURL(String resourcePath)
 243   
     {
 244  58
         if (_assetDir == null)
 245  58
             return null;
 246   
 
 247  0
         synchronized (_resources)
 248   
         {
 249  0
             String result = (String) _resources.get(resourcePath);
 250   
 
 251  0
             if (result != null)
 252  0
                 return result;
 253   
 
 254  0
             try
 255   
             {
 256  0
                 externalize(resourcePath);
 257   
             }
 258   
             catch (IOException ex)
 259   
             {
 260  0
                 throw new ApplicationRuntimeException(Tapestry.format(
 261   
                         "AssetExternalizer.externalize-failure",
 262   
                         resourcePath,
 263   
                         _assetDir), ex);
 264   
             }
 265   
 
 266  0
             result = _URL + resourcePath;
 267   
 
 268  0
             _resources.put(resourcePath, result);
 269   
 
 270  0
             return result;
 271   
         }
 272   
     }
 273   
 
 274   
     /** @since 4.0 */
 275  27
     public void setLog(Log log)
 276   
     {
 277  27
         _log = log;
 278   
     }
 279   
 
 280   
     /** @since 4.0 */
 281  27
     public void setClassResolver(ClassResolver resolver)
 282   
     {
 283  27
         _resolver = resolver;
 284   
     }
 285   
 
 286   
     /** since 4.0 */
 287  27
     public void setPropertySource(IPropertySource propertySource)
 288   
     {
 289  27
         _propertySource = propertySource;
 290   
     }
 291   
 }