1 package org.apache.maven.plugin.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 java.io.BufferedInputStream;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.InputStream;
27 import java.util.Hashtable;
28 import java.util.Vector;
29
30 import org.apache.commons.collections.ExtendedProperties;
31 import org.apache.velocity.exception.ResourceNotFoundException;
32 import org.apache.velocity.runtime.resource.Resource;
33 import org.apache.velocity.runtime.resource.loader.ResourceLoader;
34 import org.apache.velocity.util.StringUtils;
35
36 /**
37 * Resource Loader for external projects.
38 *
39 * @version $Id: ProjectResourceLoader.html 816584 2012-05-08 12:33:35Z hboutemy $
40 */
41 public class ProjectResourceLoader
42 extends ResourceLoader
43 {
44 /**
45 * The paths to search for templates.
46 */
47 private Vector paths = null;
48
49 /**
50 * Used to map the path that a template was found on
51 * so that we can properly check the modification
52 * times of the files.
53 */
54 private Hashtable templatePaths = new Hashtable();
55
56 public void init( ExtendedProperties configuration )
57 {
58 rsvc.info( "ProjectResourceLoader : initialization starting." );
59
60 String separator = System.getProperty( "file.separator" );
61
62 String path = System.getProperty( "user.dir" ) + separator + "src" + separator + "main" + separator
63 + "resources" + separator;
64
65 rsvc.info( "path :" + path );
66
67 paths = new Vector();
68
69 paths.add( path );
70
71 int sz = paths.size();
72
73 for ( int i = 0; i < sz; i++ )
74 {
75 rsvc.info( "ProjectResourceLoader : adding path '" + (String) paths.get( i ) + "'" );
76 }
77 rsvc.info( "ProjectResourceLoader : initialization complete." );
78 }
79
80 /**
81 * Get an InputStream so that the Runtime can build a
82 * template with it.
83 *
84 * @param templateName name of template to get
85 * @return InputStream containing the template
86 * @throws ResourceNotFoundException if template not found
87 * in the file template path.
88 */
89 public synchronized InputStream getResourceStream( String templateName )
90 throws ResourceNotFoundException
91 {
92 /*
93 * Make sure we have a valid templateName.
94 */
95 if ( templateName == null || templateName.length() == 0 )
96 {
97 /*
98 * If we don't get a properly formed templateName then
99 * there's not much we can do. So we'll forget about
100 * trying to search any more paths for the template.
101 */
102 throw new ResourceNotFoundException( "Need to specify a file name or file path!" );
103 }
104
105 String template = StringUtils.normalizePath( templateName );
106 if ( template == null || template.length() == 0 )
107 {
108 String msg = "Project Resource loader error : argument " + template
109 + " contains .. and may be trying to access " + "content outside of template root. Rejected.";
110
111 rsvc.error( "ProjectResourceLoader : " + msg );
112
113 throw new ResourceNotFoundException( msg );
114 }
115
116 /*
117 * if a / leads off, then just nip that :)
118 */
119 if ( template.startsWith( "/" ) )
120 {
121 template = template.substring( 1 );
122 }
123
124 int size = paths.size();
125 for ( int i = 0; i < size; i++ )
126 {
127 String path = (String) paths.get( i );
128 InputStream inputStream = findTemplate( path, template );
129
130 if ( inputStream != null )
131 {
132 /*
133 * Store the path that this template came
134 * from so that we can check its modification
135 * time.
136 */
137
138 templatePaths.put( templateName, path );
139 return inputStream;
140 }
141 }
142
143 /*
144 * We have now searched all the paths for
145 * templates and we didn't find anything so
146 * throw an exception.
147 */
148 String msg = "ProjectResourceLoader Error: cannot find resource " + template;
149
150 throw new ResourceNotFoundException( msg );
151 }
152
153 /**
154 * Try to find a template given a normalized path.
155 *
156 * @param path a normalized path
157 * @return InputStream input stream that will be parsed
158 *
159 */
160 private InputStream findTemplate( String path, String template )
161 {
162 try
163 {
164 File file = new File( path, template );
165
166 if ( file.canRead() )
167 {
168 return new BufferedInputStream( new FileInputStream( file.getAbsolutePath() ) );
169 }
170 else
171 {
172 return null;
173 }
174 }
175 catch ( FileNotFoundException fnfe )
176 {
177 /*
178 * log and convert to a general Velocity ResourceNotFoundException
179 */
180 return null;
181 }
182 }
183
184 /**
185 * How to keep track of all the modified times
186 * across the paths. Note that a file might have
187 * appeared in a directory which is earlier in the
188 * path; so we should search the path and see if
189 * the file we find that way is the same as the one
190 * that we have cached.
191 */
192 public boolean isSourceModified( Resource resource )
193 {
194 /*
195 * we assume that the file needs to be reloaded;
196 * if we find the original file and it's unchanged,
197 * then we'll flip this.
198 */
199 boolean modified = true;
200
201 String fileName = resource.getName();
202 String path = (String) templatePaths.get( fileName );
203 File currentFile = null;
204
205 for ( int i = 0; currentFile == null && i < paths.size(); i++ )
206 {
207 String testPath = (String) paths.get( i );
208 File testFile = new File( testPath, fileName );
209 if ( testFile.canRead() )
210 {
211 currentFile = testFile;
212 }
213 }
214 File file = new File( path, fileName );
215 if ( currentFile == null || !file.exists() )
216 {
217 /*
218 * noop: if the file is missing now (either the cached
219 * file is gone, or the file can no longer be found)
220 * then we leave modified alone (it's set to true); a
221 * reload attempt will be done, which will either use
222 * a new template or fail with an appropriate message
223 * about how the file couldn't be found.
224 */
225 }
226 else if ( currentFile.equals( file ) && file.canRead() )
227 {
228 /*
229 * if only if currentFile is the same as file and
230 * file.lastModified() is the same as
231 * resource.getLastModified(), then we should use the
232 * cached version.
233 */
234 modified = ( file.lastModified() != resource.getLastModified() );
235 }
236
237 /*
238 * rsvc.debug("isSourceModified for " + fileName + ": " + modified);
239 */
240 return modified;
241 }
242
243 public long getLastModified( Resource resource )
244 {
245 String path = (String) templatePaths.get( resource.getName() );
246 File file = new File( path, resource.getName() );
247
248 if ( file.canRead() )
249 {
250 return file.lastModified();
251 }
252 else
253 {
254 return 0;
255 }
256 }
257 }