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 }