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