• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package org.apache.velocity.runtime.resource.loader;
2 
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *   http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21 
22 import org.apache.velocity.exception.ResourceNotFoundException;
23 import org.apache.velocity.exception.VelocityException;
24 import org.apache.velocity.io.UnicodeInputStream;
25 import org.apache.velocity.runtime.RuntimeConstants;
26 import org.apache.velocity.runtime.RuntimeServices;
27 import org.apache.velocity.runtime.resource.Resource;
28 import org.apache.velocity.runtime.resource.ResourceCacheImpl;
29 import org.apache.velocity.util.ExtProperties;
30 
31 import org.slf4j.Logger;
32 
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.io.InputStreamReader;
36 import java.io.Reader;
37 import java.io.UnsupportedEncodingException;
38 
39 /**
40  * This is abstract class the all text resource loaders should
41  * extend.
42  *
43  * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
44  * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
45  * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
46  * @version $Id$
47  */
48 public abstract class ResourceLoader
49 {
50     /**
51      * Does this loader want templates produced with it
52      * cached in the Runtime.
53      */
54     protected boolean isCachingOn = false;
55 
56     /**
57      * This property will be passed on to the templates
58      * that are created with this loader.
59      */
60     protected long modificationCheckInterval = 2;
61 
62     /**
63      * Class name for this loader, for logging/debuggin
64      * purposes.
65      */
66     protected String className = null;
67 
68     protected RuntimeServices rsvc = null;
69     protected Logger log = null;
70 
71     /**
72      * This initialization is used by all resource
73      * loaders and must be called to set up common
74      * properties shared by all resource loaders
75      *
76      * @param rs
77      * @param configuration
78      */
commonInit(RuntimeServices rs, ExtProperties configuration)79     public void commonInit(RuntimeServices rs, ExtProperties configuration)
80     {
81         this.rsvc = rs;
82         String loaderName = configuration.getString(RuntimeConstants.RESOURCE_LOADER_IDENTIFIER);
83         log = rsvc.getLog("loader." + (loaderName == null ? this.getClass().getSimpleName() : loaderName));
84 
85         /*
86          *  these two properties are not required for all loaders.
87          *  For example, for ClasspathLoader, what would cache mean?
88          *  so adding default values which I think are the safest
89          *
90          *  don't cache, and modCheckInterval irrelevant...
91          */
92 
93         try
94         {
95             isCachingOn = configuration.getBoolean(RuntimeConstants.RESOURCE_LOADER_CACHE, false);
96         }
97         catch (Exception e)
98         {
99             isCachingOn = false;
100             String msg = "Exception parsing cache setting: " + configuration.getString(RuntimeConstants.RESOURCE_LOADER_CACHE);
101             log.error(msg, e);
102             throw new VelocityException(msg, e);
103         }
104         try
105         {
106             modificationCheckInterval = configuration.getLong(RuntimeConstants.RESOURCE_LOADER_CHECK_INTERVAL, 0);
107         }
108         catch (Exception e)
109         {
110             modificationCheckInterval = 0;
111             String msg = "Exception parsing modificationCheckInterval setting: " + RuntimeConstants.RESOURCE_LOADER_CHECK_INTERVAL;
112             log.error(msg, e);
113             throw new VelocityException(msg, e);
114         }
115 
116         /*
117          * this is a must!
118          */
119         className = ResourceCacheImpl.class.getName();
120         try
121         {
122             className = configuration.getString(RuntimeConstants.RESOURCE_LOADER_CLASS, className);
123         }
124         catch (Exception e)
125         {
126             String msg = "Exception retrieving resource cache class name";
127             log.error(msg, e);
128             throw new VelocityException(msg, e);
129         }
130     }
131 
132     /**
133      * Initialize the template loader with a
134      * a resources class.
135      *
136      * @param configuration
137      */
init(ExtProperties configuration)138     public abstract void init(ExtProperties configuration);
139 
140     /**
141      * Get the Reader that the Runtime will parse
142      * to create a template.
143      *
144      * @param source
145      * @param encoding
146      * @return The reader for the requested resource.
147      * @throws ResourceNotFoundException
148      * @since 2.0
149      */
getResourceReader(String source, String encoding)150     public abstract Reader getResourceReader(String source, String encoding)
151             throws ResourceNotFoundException;
152 
153     /**
154      * Given a template, check to see if the source of InputStream
155      * has been modified.
156      *
157      * @param resource
158      * @return True if the resource has been modified.
159      */
isSourceModified(Resource resource)160     public abstract boolean isSourceModified(Resource resource);
161 
162     /**
163      * Get the last modified time of the InputStream source
164      * that was used to create the template. We need the template
165      * here because we have to extract the name of the template
166      * in order to locate the InputStream source.
167      *
168      * @param resource
169      * @return Time in millis when the resource has been modified.
170      */
getLastModified(Resource resource)171     public abstract long getLastModified(Resource resource);
172 
173     /**
174      * Return the class name of this resource Loader
175      *
176      * @return Class name of the resource loader.
177      */
getClassName()178     public String getClassName()
179     {
180         return className;
181     }
182 
183     /**
184      * Set the caching state. If true, then this loader
185      * would like the Runtime to cache templates that
186      * have been created with InputStreams provided
187      * by this loader.
188      *
189      * @param value
190      */
setCachingOn(boolean value)191     public void setCachingOn(boolean value)
192     {
193         isCachingOn = value;
194     }
195 
196     /**
197      * The Runtime uses this to find out whether this
198      * template loader wants the Runtime to cache
199      * templates created with InputStreams provided
200      * by this loader.
201      *
202      * @return True if this resource loader caches.
203      */
isCachingOn()204     public boolean isCachingOn()
205     {
206         return isCachingOn;
207     }
208 
209     /**
210      * Set the interval at which the InputStream source
211      * should be checked for modifications.
212      *
213      * @param modificationCheckInterval
214      */
setModificationCheckInterval(long modificationCheckInterval)215     public void setModificationCheckInterval(long modificationCheckInterval)
216     {
217         this.modificationCheckInterval = modificationCheckInterval;
218     }
219 
220     /**
221      * Get the interval at which the InputStream source
222      * should be checked for modifications.
223      *
224      * @return The modification check interval.
225      */
getModificationCheckInterval()226     public long getModificationCheckInterval()
227     {
228         return modificationCheckInterval;
229     }
230 
231     /**
232      * Check whether any given resource exists. This is not really
233      * a very efficient test and it can and should be overridden in the
234      * subclasses extending ResourceLoader2.
235      *
236      * @param resourceName The name of a resource.
237      * @return true if a resource exists and can be accessed.
238      * @since 1.6
239      */
resourceExists(final String resourceName)240     public boolean resourceExists(final String resourceName)
241     {
242         Reader reader = null;
243         try
244         {
245             reader = getResourceReader(resourceName, null);
246         }
247         catch (ResourceNotFoundException e)
248         {
249             log.debug("Could not load resource '{}' from ResourceLoader {}",
250                       resourceName, this.getClass().getName());
251         }
252         finally
253         {
254             try
255             {
256                 if (reader != null)
257                 {
258                     reader.close();
259                 }
260             }
261             catch (Exception e)
262             {
263                 String msg = "While closing InputStream for resource '" +
264                     resourceName + "' from ResourceLoader " +
265                     this.getClass().getName();
266                 log.error(msg, e);
267                 throw new VelocityException(msg, e);
268             }
269         }
270         return (reader != null);
271     }
272 
273     /**
274      * Builds a Reader given a raw InputStream and an encoding. Should be use
275      * by every subclass that whishes to accept optional BOMs in resources.
276      * This method does *not* close the given input stream whenever an exception is thrown.
277      *
278      * @param rawStream The raw input stream.
279      * @param encoding  The asked encoding.
280      * @return found reader
281      * @throws IOException
282      * @throws UnsupportedEncodingException
283      * @since 2.0
284      */
buildReader(InputStream rawStream, String encoding)285     protected Reader buildReader(InputStream rawStream, String encoding)
286             throws IOException
287     {
288         UnicodeInputStream inputStream = new UnicodeInputStream(rawStream);
289         /*
290          * Check encoding
291          */
292         String foundEncoding = inputStream.getEncodingFromStream();
293         if (foundEncoding != null && encoding != null && !UnicodeInputStream.sameEncoding(foundEncoding, encoding))
294         {
295             log.warn("Found BOM encoding '{}' differs from asked encoding: '{}' - using BOM encoding to read resource.", foundEncoding, encoding);
296             encoding = foundEncoding;
297         }
298         if (encoding == null)
299         {
300             if (foundEncoding == null)
301             {
302                 encoding = rsvc.getString(RuntimeConstants.INPUT_ENCODING);
303             } else
304             {
305                 encoding = foundEncoding;
306             }
307         }
308 
309         try
310         {
311             return new InputStreamReader(inputStream, encoding);
312         }
313         catch (UnsupportedEncodingException uee)
314         {
315             try
316             {
317                 inputStream.close();
318             }
319             catch (IOException ioe) {}
320             throw uee;
321         }
322     }
323 
324 }
325