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