1 | package org.apache.velocity.runtime.resource.loader; |
2 | |
3 | /* |
4 | * Copyright 2001,2004 The Apache Software Foundation. |
5 | * |
6 | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | * you may not use this file except in compliance with the License. |
8 | * You may obtain a copy of the License at |
9 | * |
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | * |
12 | * Unless required by applicable law or agreed to in writing, software |
13 | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | * See the License for the specific language governing permissions and |
16 | * limitations under the License. |
17 | */ |
18 | |
19 | import java.io.InputStream; |
20 | |
21 | import java.util.Hashtable; |
22 | import java.util.Vector; |
23 | |
24 | import org.apache.velocity.util.StringUtils; |
25 | import org.apache.velocity.runtime.resource.Resource; |
26 | import org.apache.velocity.exception.ResourceNotFoundException; |
27 | import org.apache.commons.collections.ExtendedProperties; |
28 | |
29 | /** |
30 | * <p> |
31 | * ResourceLoader to load templates from multiple Jar files. |
32 | * </p> |
33 | * <p> |
34 | * The configuration of the JarResourceLoader is straightforward - |
35 | * You simply add the JarResourceLoader to the configuration via |
36 | * </p> |
37 | * <p><pre> |
38 | * resource.loader = jar |
39 | * jar.resource.loader.class = org.apache.velocity.runtime.resource.loader.JarResourceLoader |
40 | * jar.resource.loader.path = list of JAR <URL>s |
41 | * </pre></p> |
42 | * |
43 | * <p> So for example, if you had a jar file on your local filesystem, you could simply do |
44 | * <pre> |
45 | * jar.resource.loader.path = jar:file:/opt/myfiles/jar1.jar |
46 | * </pre> |
47 | * </p> |
48 | * <p> Note that jar specification for the <code>.path</code> configuration property |
49 | * conforms to the same rules for the java.net.JarUrlConnection class. |
50 | * </p> |
51 | * |
52 | * <p> For a working example, see the unit test case, |
53 | * org.apache.velocity.test.MultiLoaderTestCase class |
54 | * </p> |
55 | * |
56 | * @author <a href="mailto:daveb@miceda-data.com">Dave Bryson</a> |
57 | * @version $Id: JarResourceLoader.java,v 1.16.4.1 2004/03/03 23:23:02 geirm Exp $ |
58 | */ |
59 | public class JarResourceLoader extends ResourceLoader |
60 | { |
61 | /** |
62 | * Maps entries to the parent JAR File |
63 | * Key = the entry *excluding* plain directories |
64 | * Value = the JAR URL |
65 | */ |
66 | private Hashtable entryDirectory = new Hashtable(559); |
67 | |
68 | /** |
69 | * Maps JAR URLs to the actual JAR |
70 | * Key = the JAR URL |
71 | * Value = the JAR |
72 | */ |
73 | private Hashtable jarfiles = new Hashtable(89); |
74 | |
75 | /** |
76 | * Called by Velocity to initialize the loader |
77 | */ |
78 | public void init( ExtendedProperties configuration) |
79 | { |
80 | rsvc.info("JarResourceLoader : initialization starting."); |
81 | |
82 | Vector paths = configuration.getVector("path"); |
83 | |
84 | /* |
85 | * support the old version but deprecate with a log message |
86 | */ |
87 | |
88 | if( paths == null || paths.size() == 0) |
89 | { |
90 | paths = configuration.getVector("resource.path"); |
91 | |
92 | if (paths != null && paths.size() > 0) |
93 | { |
94 | rsvc.warn("JarResourceLoader : you are using a deprecated configuration" |
95 | + " property for the JarResourceLoader -> '<name>.resource.loader.resource.path'." |
96 | + " Please change to the conventional '<name>.resource.loader.path'."); |
97 | } |
98 | } |
99 | |
100 | rsvc.info("JarResourceLoader # of paths : " + paths.size() ); |
101 | |
102 | for ( int i=0; i<paths.size(); i++ ) |
103 | { |
104 | loadJar( (String)paths.get(i) ); |
105 | } |
106 | |
107 | rsvc.info("JarResourceLoader : initialization complete."); |
108 | } |
109 | |
110 | private void loadJar( String path ) |
111 | { |
112 | rsvc.info("JarResourceLoader : trying to load: " + path); |
113 | |
114 | // Check path information |
115 | if ( path == null ) |
116 | { |
117 | rsvc.error("JarResourceLoader : can not load JAR - JAR path is null"); |
118 | } |
119 | if ( !path.startsWith("jar:") ) |
120 | { |
121 | rsvc.error("JarResourceLoader : JAR path must start with jar: -> " + |
122 | "see java.net.JarURLConnection for information"); |
123 | } |
124 | if ( !path.endsWith("!/") ) |
125 | { |
126 | path += "!/"; |
127 | } |
128 | |
129 | // Close the jar if it's already open |
130 | // this is useful for a reload |
131 | closeJar( path ); |
132 | |
133 | // Create a new JarHolder |
134 | JarHolder temp = new JarHolder( rsvc, path ); |
135 | // Add it's entries to the entryCollection |
136 | addEntries( temp.getEntries() ); |
137 | // Add it to the Jar table |
138 | jarfiles.put( temp.getUrlPath(), temp ); |
139 | } |
140 | |
141 | /** |
142 | * Closes a Jar file and set its URLConnection |
143 | * to null. |
144 | */ |
145 | private void closeJar( String path ) |
146 | { |
147 | if ( jarfiles.containsKey(path) ) |
148 | { |
149 | JarHolder theJar = (JarHolder)jarfiles.get(path); |
150 | theJar.close(); |
151 | } |
152 | } |
153 | |
154 | /** |
155 | * Copy all the entries into the entryDirectory |
156 | * It will overwrite any duplicate keys. |
157 | */ |
158 | private synchronized void addEntries( Hashtable entries ) |
159 | { |
160 | entryDirectory.putAll( entries ); |
161 | } |
162 | |
163 | /** |
164 | * Get an InputStream so that the Runtime can build a |
165 | * template with it. |
166 | * |
167 | * @param name name of template to get |
168 | * @return InputStream containing the template |
169 | * @throws ResourceNotFoundException if template not found |
170 | * in the file template path. |
171 | */ |
172 | public synchronized InputStream getResourceStream( String source ) |
173 | throws ResourceNotFoundException |
174 | { |
175 | InputStream results = null; |
176 | |
177 | if ( source == null || source.length() == 0) |
178 | { |
179 | throw new ResourceNotFoundException("Need to have a resource!"); |
180 | } |
181 | |
182 | String normalizedPath = StringUtils.normalizePath( source ); |
183 | |
184 | if ( normalizedPath == null || normalizedPath.length() == 0 ) |
185 | { |
186 | String msg = "JAR resource error : argument " + normalizedPath + |
187 | " contains .. and may be trying to access " + |
188 | "content outside of template root. Rejected."; |
189 | |
190 | rsvc.error( "JarResourceLoader : " + msg ); |
191 | |
192 | throw new ResourceNotFoundException ( msg ); |
193 | } |
194 | |
195 | /* |
196 | * if a / leads off, then just nip that :) |
197 | */ |
198 | if ( normalizedPath.startsWith("/") ) |
199 | { |
200 | normalizedPath = normalizedPath.substring(1); |
201 | } |
202 | |
203 | if ( entryDirectory.containsKey( normalizedPath ) ) |
204 | { |
205 | String jarurl = (String)entryDirectory.get( normalizedPath ); |
206 | |
207 | if ( jarfiles.containsKey( jarurl ) ) |
208 | { |
209 | JarHolder holder = (JarHolder)jarfiles.get( jarurl ); |
210 | results = holder.getResource( normalizedPath ); |
211 | return results; |
212 | } |
213 | } |
214 | |
215 | throw new ResourceNotFoundException( "JarResourceLoader Error: cannot find resource " + |
216 | source ); |
217 | |
218 | } |
219 | |
220 | |
221 | // TO DO BELOW |
222 | // SHOULD BE DELEGATED TO THE JARHOLDER |
223 | public boolean isSourceModified(Resource resource) |
224 | { |
225 | return true; |
226 | } |
227 | |
228 | public long getLastModified(Resource resource) |
229 | { |
230 | return 0; |
231 | } |
232 | } |
233 | |
234 | |
235 | |
236 | |
237 | |
238 | |
239 | |
240 | |
241 | |
242 | |