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: 297   Methods: 12
NCLOC: 155   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
AssetService.java 50% 84.4% 100% 80.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.IOException;
 19   
 import java.io.InputStream;
 20   
 import java.io.OutputStream;
 21   
 import java.net.URL;
 22   
 import java.net.URLConnection;
 23   
 import java.util.HashMap;
 24   
 import java.util.Map;
 25   
 
 26   
 import org.apache.hivemind.ApplicationRuntimeException;
 27   
 import org.apache.hivemind.ClassResolver;
 28   
 import org.apache.hivemind.util.Defense;
 29   
 import org.apache.hivemind.util.IOUtils;
 30   
 import org.apache.tapestry.IRequestCycle;
 31   
 import org.apache.tapestry.Tapestry;
 32   
 import org.apache.tapestry.engine.IEngineService;
 33   
 import org.apache.tapestry.engine.ILink;
 34   
 import org.apache.tapestry.error.RequestExceptionReporter;
 35   
 import org.apache.tapestry.link.StaticLink;
 36   
 import org.apache.tapestry.services.LinkFactory;
 37   
 import org.apache.tapestry.services.ServiceConstants;
 38   
 import org.apache.tapestry.util.ContentType;
 39   
 import org.apache.tapestry.web.WebContext;
 40   
 import org.apache.tapestry.web.WebResponse;
 41   
 
 42   
 /**
 43   
  * A service for building URLs to and accessing {@link org.apache.tapestry.IAsset}s. Most of the
 44   
  * work is deferred to the {@link org.apache.tapestry.IAsset}instance.
 45   
  * <p>
 46   
  * The retrieval part is directly linked to {@link PrivateAsset}. The service responds to a URL
 47   
  * that encodes the path of a resource within the classpath. The {@link #service(IRequestCycle)}
 48   
  * method reads the resource and streams it out.
 49   
  * <p>
 50   
  * TBD: Security issues. Should only be able to retrieve a resource that was previously registerred
 51   
  * in some way ... otherwise, hackers will be able to suck out the .class files of the application!
 52   
  * 
 53   
  * @author Howard Lewis Ship
 54   
  */
 55   
 
 56   
 public class AssetService implements IEngineService
 57   
 {
 58   
 
 59   
     /** @since 4.0 */
 60   
     private ClassResolver _classResolver;
 61   
 
 62   
     /** @since 4.0 */
 63   
     private AssetExternalizer _assetExternalizer;
 64   
 
 65   
     /** @since 4.0 */
 66   
     private LinkFactory _linkFactory;
 67   
 
 68   
     /** @since 4.0 */
 69   
     private WebContext _context;
 70   
 
 71   
     /** @since 4.0 */
 72   
     private WebResponse _response;
 73   
 
 74   
     /** @since 4.0 */
 75   
     private ResourceDigestSource _digestSource;
 76   
 
 77   
     /**
 78   
      * Defaults MIME types, by extension, used when the servlet container doesn't provide MIME
 79   
      * types. ServletExec Debugger, for example, fails to provide these.
 80   
      */
 81   
 
 82   
     private final static Map _mimeTypes;
 83   
 
 84   
     static
 85   
     {
 86  1
         _mimeTypes = new HashMap(17);
 87  1
         _mimeTypes.put("css", "text/css");
 88  1
         _mimeTypes.put("gif", "image/gif");
 89  1
         _mimeTypes.put("jpg", "image/jpeg");
 90  1
         _mimeTypes.put("jpeg", "image/jpeg");
 91  1
         _mimeTypes.put("htm", "text/html");
 92  1
         _mimeTypes.put("html", "text/html");
 93   
     }
 94   
 
 95   
     private static final int BUFFER_SIZE = 10240;
 96   
 
 97   
     /** @since 4.0 */
 98   
 
 99   
     private RequestExceptionReporter _exceptionReporter;
 100   
 
 101   
     /**
 102   
      * Query parameter that stores the path to the resource (with a leading slash).
 103   
      * 
 104   
      * @since 4.0
 105   
      */
 106   
 
 107   
     public static final String PATH = "path";
 108   
 
 109   
     /**
 110   
      * Query parameter that stores the digest for the file; this is used to authenticate that the
 111   
      * client is allowed to access the file.
 112   
      * 
 113   
      * @since 4.0
 114   
      */
 115   
 
 116   
     public static final String DIGEST = "digest";
 117   
 
 118   
     /**
 119   
      * Builds a {@link ILink}for a {@link PrivateAsset}.
 120   
      * <p>
 121   
      * A single parameter is expected, the resource path of the asset (which is expected to start
 122   
      * with a leading slash).
 123   
      */
 124   
 
 125  58
     public ILink getLink(IRequestCycle cycle, Object parameter)
 126   
     {
 127  58
         Defense.isAssignable(parameter, String.class, "parameter");
 128   
 
 129  58
         String path = (String) parameter;
 130   
 
 131  58
         String externalURL = _assetExternalizer.getURL(path);
 132   
 
 133  58
         if (externalURL != null)
 134  0
             return new StaticLink(externalURL);
 135   
 
 136  58
         String digest = _digestSource.getDigestForResource(path);
 137   
 
 138  58
         Map parameters = new HashMap();
 139   
 
 140  58
         parameters.put(ServiceConstants.SERVICE, Tapestry.ASSET_SERVICE);
 141  58
         parameters.put(PATH, path);
 142  58
         parameters.put(DIGEST, digest);
 143   
 
 144   
         // Service is stateless, which is the exception to the rule.
 145   
 
 146  58
         return _linkFactory.constructLink(cycle, parameters, false);
 147   
     }
 148   
 
 149  27
     public String getName()
 150   
     {
 151  27
         return Tapestry.ASSET_SERVICE;
 152   
     }
 153   
 
 154  1
     private String getMimeType(String path)
 155   
     {
 156  1
         String result = _context.getMimeType(path);
 157   
 
 158  1
         if (result == null)
 159   
         {
 160  0
             int dotx = path.lastIndexOf('.');
 161  0
             String key = path.substring(dotx + 1).toLowerCase();
 162   
 
 163  0
             result = (String) _mimeTypes.get(key);
 164   
 
 165  0
             if (result == null)
 166  0
                 result = "text/plain";
 167   
         }
 168   
 
 169  1
         return result;
 170   
     }
 171   
 
 172   
     /**
 173   
      * Retrieves a resource from the classpath and returns it to the client in a binary output
 174   
      * stream.
 175   
      * <p>
 176   
      * TBD: Security issues. Hackers can download .class files.
 177   
      */
 178   
 
 179  1
     public void service(IRequestCycle cycle) throws IOException
 180   
     {
 181  1
         String path = cycle.getParameter(PATH);
 182  1
         String md5 = cycle.getParameter(DIGEST);
 183   
 
 184  1
         try
 185   
         {
 186  1
             if (!_digestSource.getDigestForResource(path).equals(md5))
 187  0
                 throw new ApplicationRuntimeException(AssetMessages.md5Mismatch(path));
 188   
 
 189  1
             URL resourceURL = _classResolver.getResource(path);
 190   
 
 191  1
             if (resourceURL == null)
 192  0
                 throw new ApplicationRuntimeException(AssetMessages.noSuchResource(path));
 193   
 
 194  1
             URLConnection resourceConnection = resourceURL.openConnection();
 195   
 
 196  1
             writeAssetContent(cycle, path, resourceConnection);
 197   
         }
 198   
         catch (Throwable ex)
 199   
         {
 200  0
             _exceptionReporter.reportRequestException(AssetMessages.exceptionReportTitle(path), ex);
 201   
         }
 202   
 
 203   
     }
 204   
 
 205   
     /** @since 2.2 */
 206   
 
 207  1
     private void writeAssetContent(IRequestCycle cycle, String resourcePath,
 208   
             URLConnection resourceConnection) throws IOException
 209   
     {
 210  1
         InputStream input = null;
 211   
 
 212  1
         try
 213   
         {
 214   
             // Getting the content type and length is very dependant
 215   
             // on support from the application server (represented
 216   
             // here by the servletContext).
 217   
 
 218  1
             String contentType = getMimeType(resourcePath);
 219  1
             int contentLength = resourceConnection.getContentLength();
 220   
 
 221  1
             if (contentLength > 0)
 222  1
                 _response.setContentLength(contentLength);
 223   
 
 224   
             // Set the content type. If the servlet container doesn't
 225   
             // provide it, try and guess it by the extension.
 226   
 
 227  1
             if (contentType == null || contentType.length() == 0)
 228  0
                 contentType = getMimeType(resourcePath);
 229   
 
 230  1
             OutputStream output = _response.getOutputStream(new ContentType(contentType));
 231   
 
 232  1
             input = new BufferedInputStream(resourceConnection.getInputStream());
 233   
 
 234  1
             byte[] buffer = new byte[BUFFER_SIZE];
 235   
 
 236  1
             while (true)
 237   
             {
 238  2
                 int bytesRead = input.read(buffer);
 239   
 
 240  2
                 if (bytesRead < 0)
 241  1
                     break;
 242   
 
 243  1
                 output.write(buffer, 0, bytesRead);
 244   
             }
 245   
 
 246  1
             input.close();
 247  1
             input = null;
 248   
         }
 249   
         finally
 250   
         {
 251  1
             IOUtils.close(input);
 252   
         }
 253   
     }
 254   
 
 255   
     /** @since 4.0 */
 256   
 
 257  27
     public void setExceptionReporter(RequestExceptionReporter exceptionReporter)
 258   
     {
 259  27
         _exceptionReporter = exceptionReporter;
 260   
     }
 261   
 
 262   
     /** @since 4.0 */
 263  27
     public void setAssetExternalizer(AssetExternalizer assetExternalizer)
 264   
     {
 265  27
         _assetExternalizer = assetExternalizer;
 266   
     }
 267   
 
 268   
     /** @since 4.0 */
 269  27
     public void setLinkFactory(LinkFactory linkFactory)
 270   
     {
 271  27
         _linkFactory = linkFactory;
 272   
     }
 273   
 
 274   
     /** @since 4.0 */
 275  27
     public void setClassResolver(ClassResolver classResolver)
 276   
     {
 277  27
         _classResolver = classResolver;
 278   
     }
 279   
 
 280   
     /** @since 4.0 */
 281  27
     public void setContext(WebContext context)
 282   
     {
 283  27
         _context = context;
 284   
     }
 285   
 
 286   
     /** @since 4.0 */
 287  27
     public void setResponse(WebResponse response)
 288   
     {
 289  27
         _response = response;
 290   
     }
 291   
 
 292   
     /** @since 4.0 */
 293  27
     public void setDigestSource(ResourceDigestSource md5Source)
 294   
     {
 295  27
         _digestSource = md5Source;
 296   
     }
 297   
 }