Clover coverage report - Code Coverage for tapestry release 3.1-alpha-1
Coverage timestamp: Mon Feb 21 2005 09:16:14 EST
file stats: LOC: 239   Methods: 9
NCLOC: 122   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 75% 92.5% 100% 90.5%
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.IOException;
 18   
 import java.io.InputStream;
 19   
 import java.net.URL;
 20   
 import java.net.URLConnection;
 21   
 import java.util.HashMap;
 22   
 import java.util.Map;
 23   
 
 24   
 import javax.servlet.ServletContext;
 25   
 import javax.servlet.ServletException;
 26   
 
 27   
 import org.apache.hivemind.ApplicationRuntimeException;
 28   
 import org.apache.hivemind.ClassResolver;
 29   
 import org.apache.hivemind.util.Defense;
 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.link.StaticLink;
 35   
 import org.apache.tapestry.request.ResponseOutputStream;
 36   
 import org.apache.tapestry.services.LinkFactory;
 37   
 import org.apache.tapestry.services.RequestExceptionReporter;
 38   
 import org.apache.tapestry.services.ServiceConstants;
 39   
 
 40   
 /**
 41   
  * A service for building URLs to and accessing {@link org.apache.tapestry.IAsset}s. Most of the
 42   
  * work is deferred to the {@link org.apache.tapestry.IAsset}instance.
 43   
  * <p>
 44   
  * The retrieval part is directly linked to {@link PrivateAsset}. The service responds to a URL
 45   
  * that encodes the path of a resource within the classpath. The
 46   
  * {@link #service(IRequestCycle, ResponseOutputStream)}method reads the resource and streams it
 47   
  * out.
 48   
  * <p>
 49   
  * TBD: Security issues. Should only be able to retrieve a resource that was previously registerred
 50   
  * in some way ... otherwise, hackers will be able to suck out the .class files of the application!
 51   
  * 
 52   
  * @author Howard Lewis Ship
 53   
  */
 54   
 
 55   
 public class AssetService implements IEngineService
 56   
 {
 57   
     /** @since 3.1 */
 58   
     private ClassResolver _classResolver;
 59   
 
 60   
     /** @since 3.1 */
 61   
     private AssetExternalizer _assetExternalizer;
 62   
 
 63   
     /** @since 3.1 */
 64   
     private LinkFactory _linkFactory;
 65   
 
 66   
     /**
 67   
      * Defaults MIME types, by extension, used when the servlet container doesn't provide MIME
 68   
      * types. ServletExec Debugger, for example, fails to do provide these.
 69   
      */
 70   
 
 71   
     private final static Map _mimeTypes;
 72   
 
 73   
     static
 74   
     {
 75  1
         _mimeTypes = new HashMap(17);
 76  1
         _mimeTypes.put("css", "text/css");
 77  1
         _mimeTypes.put("gif", "image/gif");
 78  1
         _mimeTypes.put("jpg", "image/jpeg");
 79  1
         _mimeTypes.put("jpeg", "image/jpeg");
 80  1
         _mimeTypes.put("htm", "text/html");
 81  1
         _mimeTypes.put("html", "text/html");
 82   
     }
 83   
 
 84   
     private static final int BUFFER_SIZE = 10240;
 85   
 
 86   
     /** @since 3.1 */
 87   
 
 88   
     private RequestExceptionReporter _exceptionReporter;
 89   
 
 90   
     private static final String PATH = "path";
 91   
 
 92   
     /**
 93   
      * Builds a {@link ILink}for a {@link PrivateAsset}.
 94   
      * <p>
 95   
      * A single parameter is expected, the resource path of the asset (which is expected to start
 96   
      * with a leading slash).
 97   
      */
 98   
 
 99  90
     public ILink getLink(IRequestCycle cycle, Object parameter)
 100   
     {
 101  90
         Defense.isAssignable(parameter, String.class, "parameter");
 102   
 
 103  90
         String path = (String) parameter;
 104   
 
 105  90
         String externalURL = _assetExternalizer.getURL(path);
 106   
 
 107  90
         if (externalURL != null)
 108  0
             return new StaticLink(externalURL);
 109   
 
 110  90
         Map parameters = new HashMap();
 111   
 
 112  90
         parameters.put(ServiceConstants.SERVICE, Tapestry.ASSET_SERVICE);
 113  90
         parameters.put(PATH, path);
 114   
 
 115   
         // Service is stateless
 116   
 
 117  90
         return _linkFactory.constructLink(cycle, parameters, false);
 118   
     }
 119   
 
 120  58
     public String getName()
 121   
     {
 122  58
         return Tapestry.ASSET_SERVICE;
 123   
     }
 124   
 
 125  1
     private static String getMimeType(String path)
 126   
     {
 127  1
         int dotx = path.lastIndexOf('.');
 128  1
         String key = path.substring(dotx + 1).toLowerCase();
 129   
 
 130  1
         String result = (String) _mimeTypes.get(key);
 131   
 
 132  1
         if (result == null)
 133  0
             result = "text/plain";
 134   
 
 135  1
         return result;
 136   
     }
 137   
 
 138   
     /**
 139   
      * Retrieves a resource from the classpath and returns it to the client in a binary output
 140   
      * stream.
 141   
      * <p>
 142   
      * TBD: Security issues. Hackers can download .class files.
 143   
      */
 144   
 
 145  3
     public void service(IRequestCycle cycle, ResponseOutputStream output) throws ServletException,
 146   
             IOException
 147   
     {
 148  3
         String path = cycle.getParameter(PATH);
 149   
 
 150  3
         URL resourceURL = _classResolver.getResource(path);
 151   
 
 152  3
         if (resourceURL == null)
 153  1
             throw new ApplicationRuntimeException(Tapestry.format("missing-resource", path));
 154   
 
 155  2
         URLConnection resourceConnection = resourceURL.openConnection();
 156   
 
 157  2
         ServletContext servletContext = cycle.getRequestContext().getServlet().getServletContext();
 158   
 
 159  2
         writeAssetContent(cycle, output, path, resourceConnection, servletContext);
 160   
     }
 161   
 
 162   
     /** @since 2.2 * */
 163   
 
 164  2
     private void writeAssetContent(IRequestCycle cycle, ResponseOutputStream output,
 165   
             String resourcePath, URLConnection resourceConnection, ServletContext servletContext)
 166   
     {
 167   
         // Getting the content type and length is very dependant
 168   
         // on support from the application server (represented
 169   
         // here by the servletContext).
 170   
 
 171  2
         String contentType = servletContext.getMimeType(resourcePath);
 172  2
         int contentLength = resourceConnection.getContentLength();
 173   
 
 174  2
         try
 175   
         {
 176  2
             if (contentLength > 0)
 177  2
                 cycle.getRequestContext().getResponse().setContentLength(contentLength);
 178   
 
 179   
             // Set the content type. If the servlet container doesn't
 180   
             // provide it, try and guess it by the extension.
 181   
 
 182  2
             if (contentType == null || contentType.length() == 0)
 183  1
                 contentType = getMimeType(resourcePath);
 184   
 
 185  2
             output.setContentType(contentType);
 186   
 
 187   
             // Disable any further buffering inside the ResponseOutputStream
 188   
 
 189  2
             output.forceFlush();
 190   
 
 191  2
             InputStream input = resourceConnection.getInputStream();
 192   
 
 193  2
             byte[] buffer = new byte[BUFFER_SIZE];
 194   
 
 195  2
             while (true)
 196   
             {
 197  4
                 int bytesRead = input.read(buffer);
 198   
 
 199  4
                 if (bytesRead < 0)
 200  2
                     break;
 201   
 
 202  2
                 output.write(buffer, 0, bytesRead);
 203   
             }
 204   
 
 205  2
             input.close();
 206   
         }
 207   
         catch (Throwable ex)
 208   
         {
 209  0
             String title = Tapestry.format("AssetService.exception-report-title", resourcePath);
 210   
 
 211  0
             _exceptionReporter.reportRequestException(title, ex);
 212   
         }
 213   
     }
 214   
 
 215   
     /** @since 3.1 */
 216   
 
 217  52
     public void setExceptionReporter(RequestExceptionReporter exceptionReporter)
 218   
     {
 219  52
         _exceptionReporter = exceptionReporter;
 220   
     }
 221   
 
 222   
     /** @since 3.1 */
 223  52
     public void setAssetExternalizer(AssetExternalizer assetExternalizer)
 224   
     {
 225  52
         _assetExternalizer = assetExternalizer;
 226   
     }
 227   
 
 228   
     /** @since 3.1 */
 229  52
     public void setLinkFactory(LinkFactory linkFactory)
 230   
     {
 231  52
         _linkFactory = linkFactory;
 232   
     }
 233   
 
 234   
     /** @since 3.1 */
 235  52
     public void setClassResolver(ClassResolver classResolver)
 236   
     {
 237  52
         _classResolver = classResolver;
 238   
     }
 239   
 }