View Javadoc

1   
2   /*
3    * Copyright 2001,2004 The Apache Software Foundation.
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.abstracthorizon.danube.velocity;
18  
19  import java.io.BufferedInputStream;
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.FileNotFoundException;
23  import java.io.InputStream;
24  import java.util.Hashtable;
25  import java.util.Vector;
26  
27  import org.apache.commons.collections.ExtendedProperties;
28  import org.apache.velocity.exception.ResourceNotFoundException;
29  import org.apache.velocity.runtime.resource.Resource;
30  import org.apache.velocity.runtime.resource.loader.ResourceLoader;
31  import org.apache.velocity.util.StringUtils;
32  
33  /**
34   * A loader for templates stored on the file system adapted for Danube.
35   *
36   * @author Daniel Sendula
37   * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
38   */
39  // TODO find what class is this based on
40  public class AbsoluteFileResourceLoader extends ResourceLoader {
41      /** The paths to search for templates. */
42      @SuppressWarnings("unchecked")
43      private Vector paths = null;
44  
45      /**
46       * Used to map the path that a template was found on so that we can properly
47       * check the modification times of the files.
48       */
49      private Hashtable<String, String> templatePaths = new Hashtable<String, String>();
50  
51      /**
52       * Initialises this loaded
53       * @param configuration configuration
54       */
55      public void init(ExtendedProperties configuration) {
56          rsvc.info("FileResourceLoader : initialization starting.");
57  
58          paths = configuration.getVector("path");
59  
60          /*
61           * lets tell people what paths we will be using
62           */
63  
64          int sz = paths.size();
65  
66          for (int i = 0; i < sz; i++) {
67              rsvc.info("FileResourceLoader : adding path '" + (String) paths.get(i) + "'");
68          }
69  
70          rsvc.info("FileResourceLoader : initialization complete.");
71      }
72  
73      /**
74       * Get an InputStream so that the Runtime can build a template with it.
75       *
76       * @param templateName name of template to get
77       * @return InputStream containing the template
78       * @throws ResourceNotFoundException if template not found in the file template path.
79       */
80      public synchronized InputStream getResourceStream(String templateName) throws ResourceNotFoundException {
81          /*
82           * Make sure we have a valid templateName.
83           */
84          if (templateName == null || templateName.length() == 0) {
85              /*
86               * If we don't get a properly formed templateName then there's not
87               * much we can do. So we'll forget about trying to search any more
88               * paths for the template.
89               */
90              throw new ResourceNotFoundException("Need to specify a file name or file path!");
91          }
92  
93          String template = StringUtils.normalizePath(templateName);
94          if (template == null || template.length() == 0) {
95              String msg = "File resource error : argument " + template + " contains .. and may be trying to access " + "content outside of template root.  Rejected.";
96  
97              rsvc.error("FileResourceLoader : " + msg);
98  
99              throw new ResourceNotFoundException(msg);
100         }
101 
102         /*
103          * if a / leads off, then just nip that :)
104          */
105         if (template.startsWith("/")) {
106             template = template.substring(1);
107         }
108 
109         int size = paths.size();
110         for (int i = 0; i < size; i++) {
111             String path = (String) paths.get(i);
112             InputStream inputStream = findTemplate(path, template);
113 
114             if (inputStream != null) {
115                 /*
116                  * Store the path that this template came from so that we can
117                  * check its modification time.
118                  */
119 
120                 templatePaths.put(templateName, path);
121                 return inputStream;
122             }
123         }
124 
125         /*
126          * We have now searched all the paths for templates and we didn't find
127          * anything so throw an exception.
128          */
129         String msg = "FileResourceLoader Error: cannot find resource " + template;
130 
131         throw new ResourceNotFoundException(msg);
132     }
133 
134     /**
135      * Try to find a template given a normalized path.
136      *
137      * @param String a normalized path
138      * @return InputStream input stream that will be parsed
139      *
140      */
141     private InputStream findTemplate(String path, String template) {
142         try {
143             File file = new File(path, template);
144             if ((path.length() == 0) && !file.exists()) {
145                 file = new File(template);
146             }
147 
148             if (file.canRead()) {
149                 return new BufferedInputStream(new FileInputStream(file.getAbsolutePath()));
150             } else {
151                 return null;
152             }
153         } catch (FileNotFoundException fnfe) {
154             /*
155              * log and convert to a general Velocity ResourceNotFoundException
156              */
157             return null;
158         }
159     }
160 
161     /**
162      * How to keep track of all the modified times across the paths. Note that a
163      * file might have appeared in a directory which is earlier in the path; so
164      * we should search the path and see if the file we find that way is the
165      * same as the one that we have cached.
166      * @param resource template's resource
167      * @return is source is modified
168      */
169     public boolean isSourceModified(Resource resource) {
170         /*
171          * we assume that the file needs to be reloaded; if we find the original
172          * file and it's unchanged, then we'll flip this.
173          */
174         boolean modified = true;
175 
176         String fileName = resource.getName();
177         String path = (String) templatePaths.get(fileName);
178         File currentFile = null;
179 
180         for (int i = 0; currentFile == null && i < paths.size(); i++) {
181             String testPath = (String) paths.get(i);
182             File testFile = new File(testPath, fileName);
183             if (testFile.canRead()) {
184                 currentFile = testFile;
185             }
186         }
187         File file = new File(path, fileName);
188         if (currentFile == null || !file.exists()) {
189             /*
190              * noop: if the file is missing now (either the cached file is gone,
191              * or the file can no longer be found) then we leave modified alone
192              * (it's set to true); a reload attempt will be done, which will
193              * either use a new template or fail with an appropriate message
194              * about how the file couldn't be found.
195              */
196         } else if (currentFile.equals(file) && file.canRead()) {
197             /*
198              * if only if currentFile is the same as file and
199              * file.lastModified() is the same as resource.getLastModified(),
200              * then we should use the cached version.
201              */
202             modified = (file.lastModified() != resource.getLastModified());
203         }
204 
205         /*
206          * rsvc.debug("isSourceModified for " + fileName + ": " + modified);
207          */
208         return modified;
209     }
210 
211     /**
212      * Returns when velocity template was last modified
213      * @param resource template's resource
214      * @return long template
215      */
216     public long getLastModified(Resource resource) {
217         String path = (String) templatePaths.get(resource.getName());
218         File file = new File(path, resource.getName());
219 
220         if (file.canRead()) {
221             return file.lastModified();
222         } else {
223             return 0;
224         }
225     }
226 }