001: /*
002: * Copyright 2003-2006 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: *
016: * This code was basaed on the simular code in the Velocity Tools project.
017: */
018:
019: package org.sakaiproject.search.tool;
020:
021: import java.io.File;
022: import java.io.InputStream;
023: import java.util.HashMap;
024:
025: import javax.servlet.ServletContext;
026:
027: import org.apache.commons.collections.ExtendedProperties;
028: import org.apache.velocity.exception.ResourceNotFoundException;
029: import org.apache.velocity.runtime.resource.Resource;
030: import org.apache.velocity.runtime.resource.loader.ResourceLoader;
031:
032: /**
033: * Resource loader that uses the ServletContext of a webapp to
034: * load Velocity templates. (it's much easier to use with servlets than
035: * the standard FileResourceLoader, in particular the use of war files
036: * is transparent).
037: *
038: * The default search path is '/' (relative to the webapp root), but
039: * you can change this behaviour by specifying one or more paths
040: * by mean of as many webapp.resource.loader.path properties as needed
041: * in the velocity.properties file.
042: *
043: * All paths must be relative to the root of the webapp.
044: *
045: * To enable caching and cache refreshing the webapp.resource.loader.cache and
046: * webapp.resource.loader.modificationCheckInterval properties need to be
047: * set in the velocity.properties file ... auto-reloading of global macros
048: * requires the webapp.resource.loader.cache property to be set to 'false'.
049: *
050: * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
051: * @author <a href="mailto:nathan@esha.com">Nathan Bubna</a>
052: * @author <a href="mailto:claude@savoirweb.com">Claude Brisson</a>
053: * @version $Id: WebappLoader.java 22723 2007-03-16 01:35:32Z ian@caret.cam.ac.uk $ */
054:
055: public class WebappLoader extends ResourceLoader {
056:
057: /** The root paths for templates (relative to webapp's root). */
058: protected String[] paths = null;
059: protected HashMap templatePaths = null;
060:
061: protected ServletContext servletContext = null;
062:
063: /**
064: * This is abstract in the base class, so we need it.
065: * <br>
066: * NOTE: this expects that the ServletContext has already
067: * been placed in the runtime's application attributes
068: * under its full class name (i.e. "javax.servlet.ServletContext").
069: *
070: * @param configuration the {@link ExtendedProperties} associated with
071: * this resource loader.
072: */
073: public void init(ExtendedProperties configuration) {
074: rsvc.debug("WebappLoader : initialization starting.");
075:
076: /* get configured paths */
077: paths = configuration.getStringArray("path");
078: if (paths == null || paths.length == 0) {
079: paths = new String[1];
080: paths[0] = "/";
081: } else {
082: /* make sure the paths end with a '/' */
083: for (int i = 0; i < paths.length; i++) {
084: if (!paths[i].endsWith("/")) {
085: paths[i] += '/';
086: }
087: rsvc.info("WebappLoader : added template path - '"
088: + paths[i] + "'");
089: }
090: }
091:
092: /* get the ServletContext */
093: Object obj = rsvc.getApplicationAttribute(ServletContext.class
094: .getName());
095: if (obj instanceof ServletContext) {
096: servletContext = (ServletContext) obj;
097: } else {
098: rsvc
099: .error("WebappLoader : unable to retrieve ServletContext");
100: }
101:
102: /* init the template paths map */
103: templatePaths = new HashMap();
104:
105: rsvc.debug("WebappLoader : initialization complete.");
106: }
107:
108: /**
109: * Get an InputStream so that the Runtime can build a
110: * template with it.
111: *
112: * @param name name of template to get
113: * @return InputStream containing the template
114: * @throws ResourceNotFoundException if template not found
115: * in classpath.
116: */
117: public synchronized InputStream getResourceStream(String name)
118: throws ResourceNotFoundException {
119: InputStream result = null;
120:
121: if (name == null || name.length() == 0) {
122: throw new ResourceNotFoundException(
123: "WebappLoader : No template name provided");
124: }
125:
126: /* since the paths always ends in '/',
127: * make sure the name never starts with one */
128: while (name.startsWith("/")) {
129: name = name.substring(1);
130: }
131:
132: Exception exception = null;
133: for (int i = 0; i < paths.length; i++) {
134: try {
135: result = servletContext.getResourceAsStream(paths[i]
136: + name);
137:
138: /* save the path and exit the loop if we found the template */
139: if (result != null) {
140: templatePaths.put(name, paths[i]);
141: break;
142: }
143: } catch (Exception e) {
144: /* only save the first one for later throwing */
145: if (exception == null) {
146: exception = e;
147: }
148: }
149: }
150:
151: /* if we never found the template */
152: if (result == null) {
153: String msg;
154: if (exception == null) {
155: msg = "WebappLoader : Resource '" + name
156: + "' not found.";
157: } else {
158: msg = exception.getMessage();
159: }
160: /* convert to a general Velocity ResourceNotFoundException */
161: throw new ResourceNotFoundException(msg);
162: }
163:
164: return result;
165: }
166:
167: private File getCachedFile(String rootPath, String fileName) {
168: // we do this when we cache a resource,
169: // so do it again to ensure a match
170: while (fileName.startsWith("/")) {
171: fileName = fileName.substring(1);
172: }
173:
174: String savedPath = (String) templatePaths.get(fileName);
175: return new File(rootPath + savedPath, fileName);
176: }
177:
178: /**
179: * Checks to see if a resource has been deleted, moved or modified.
180: *
181: * @param resource Resource The resource to check for modification
182: * @return boolean True if the resource has been modified
183: */
184: public boolean isSourceModified(Resource resource) {
185: String rootPath = servletContext.getRealPath("/");
186: if (rootPath == null) {
187: // rootPath is null if the servlet container cannot translate the
188: // virtual path to a real path for any reason (such as when the
189: // content is being made available from a .war archive)
190: return false;
191: }
192:
193: // first, try getting the previously found file
194: String fileName = resource.getName();
195: File cachedFile = getCachedFile(rootPath, fileName);
196: if (!cachedFile.exists()) {
197: /* then the source has been moved and/or deleted */
198: return true;
199: }
200:
201: /* check to see if the file can now be found elsewhere
202: * before it is found in the previously saved path */
203: File currentFile = null;
204: for (int i = 0; i < paths.length; i++) {
205: currentFile = new File(rootPath + paths[i], fileName);
206: if (currentFile.canRead()) {
207: /* stop at the first resource found
208: * (just like in getResourceStream()) */
209: break;
210: }
211: }
212:
213: /* if the current is the cached and it is readable */
214: if (cachedFile.equals(currentFile) && cachedFile.canRead()) {
215: /* then (and only then) do we compare the last modified values */
216: return (cachedFile.lastModified() != resource
217: .getLastModified());
218: } else {
219: /* we found a new file for the resource
220: * or the resource is no longer readable. */
221: return true;
222: }
223: }
224:
225: /**
226: * Checks to see when a resource was last modified
227: *
228: * @param resource Resource the resource to check
229: * @return long The time when the resource was last modified or 0 if the file can't be read
230: */
231: public long getLastModified(Resource resource) {
232: String rootPath = servletContext.getRealPath("/");
233: if (rootPath == null) {
234: // rootPath is null if the servlet container cannot translate the
235: // virtual path to a real path for any reason (such as when the
236: // content is being made available from a .war archive)
237: return 0;
238: }
239:
240: File cachedFile = getCachedFile(rootPath, resource.getName());
241: if (cachedFile.canRead()) {
242: return cachedFile.lastModified();
243: } else {
244: return 0;
245: }
246: }
247: }
|