View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.start;
20  
21  import java.io.File;
22  import java.io.FileFilter;
23  import java.io.IOException;
24  import java.net.URI;
25  import java.net.URISyntaxException;
26  import java.net.URL;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Collections;
30  import java.util.List;
31  import java.util.Objects;
32  import java.util.regex.Matcher;
33  import java.util.regex.Pattern;
34  
35  import org.eclipse.jetty.start.FS.RelativeRegexFilter;
36  
37  /**
38   * File access for <code>${jetty.home}</code>, <code>${jetty.base}</code>, directories.
39   * <p>
40   * By default, both <code>${jetty.home}</code> and <code>${jetty.base}</code> are the same directory, but they can point at different directories.
41   * <p>
42   * The <code>${jetty.home}</code> directory is where the main Jetty binaries and default configuration is housed.
43   * <p>
44   * The <code>${jetty.base}</code> directory is where the execution specific configuration and webapps are obtained from.
45   */
46  public class BaseHome
47  {
48      private File homeDir;
49      private File baseDir;
50  
51      public BaseHome()
52      {
53          try
54          {
55              this.baseDir = new File(System.getProperty("jetty.base",System.getProperty("user.dir",".")));
56              URL jarfile = this.getClass().getClassLoader().getResource("org/eclipse/jetty/start/BaseHome.class");
57              if (jarfile != null)
58              {
59                  Matcher m = Pattern.compile("jar:(file:.*)!/org/eclipse/jetty/start/BaseHome.class").matcher(jarfile.toString());
60                  if (m.matches())
61                  {
62                      homeDir = new File(new URI(m.group(1))).getParentFile();
63                  }
64              }
65              homeDir = new File(System.getProperty("jetty.home",(homeDir == null?baseDir:homeDir).getAbsolutePath()));
66  
67              baseDir = baseDir.getAbsoluteFile().getCanonicalFile();
68              homeDir = homeDir.getAbsoluteFile().getCanonicalFile();
69          }
70          catch (IOException | URISyntaxException e)
71          {
72              throw new RuntimeException(e);
73          }
74      }
75  
76      public BaseHome(File homeDir, File baseDir)
77      {
78          try
79          {
80              this.homeDir = homeDir.getCanonicalFile();
81              this.baseDir = baseDir == null?this.homeDir:baseDir.getCanonicalFile();
82          }
83          catch (IOException e)
84          {
85              throw new RuntimeException(e);
86          }
87      }
88  
89      public String getBase()
90      {
91          if (baseDir == null)
92          {
93              return null;
94          }
95          return baseDir.getAbsolutePath();
96      }
97  
98      public File getBaseDir()
99      {
100         return baseDir;
101     }
102 
103     /**
104      * Create a file reference to some content in <code>"${jetty.base}"</code>
105      * 
106      * @param path
107      *            the path to reference
108      * @return the file reference
109      */
110     public File getBaseFile(String path)
111     {
112         return new File(baseDir,FS.separators(path));
113     }
114 
115     /**
116      * Get a specific file reference.
117      * <p>
118      * File references go through 3 possibly scenarios.
119      * <ol>
120      * <li>If exists relative to <code>${jetty.base}</code>, return that reference</li>
121      * <li>If exists relative to <code>${jetty.home}</code>, return that reference</li>
122      * <li>Otherwise return absolute path reference</li>
123      * </ol>
124      * 
125      * @param path
126      *            the path to get.
127      * @return the file reference.
128      */
129     public File getFile(String path)
130     {
131         String rpath = FS.separators(path);
132 
133         // Relative to Base Directory First
134         if (isBaseDifferent())
135         {
136             File file = new File(baseDir,rpath);
137             if (file.exists())
138             {
139                 return file;
140             }
141         }
142 
143         // Then relative to Home Directory
144         File file = new File(homeDir,rpath);
145         if (file.exists())
146         {
147             return file;
148         }
149 
150         // Finally, as an absolute path
151         return new File(rpath);
152     }
153 
154     public String getHome()
155     {
156         return homeDir.getAbsolutePath();
157     }
158 
159     public File getHomeDir()
160     {
161         return homeDir;
162     }
163 
164     public void initialize(StartArgs args)
165     {
166         Pattern jetty_home = Pattern.compile("(-D)?jetty.home=(.*)");
167         Pattern jetty_base = Pattern.compile("(-D)?jetty.base=(.*)");
168 
169         File homePath = null;
170         File basePath = null;
171 
172         for (String arg : args.getCommandLine())
173         {
174             Matcher home_match = jetty_home.matcher(arg);
175             if (home_match.matches())
176             {
177                 homePath = new File(home_match.group(2));
178             }
179             Matcher base_match = jetty_base.matcher(arg);
180             if (base_match.matches())
181             {
182                 basePath = new File(base_match.group(2));
183             }
184         }
185 
186         if (homePath != null)
187         {
188             // logic if home is specified
189             this.homeDir = homePath.getAbsoluteFile();
190             if (basePath == null)
191             {
192                 this.baseDir = homePath.getAbsoluteFile();
193                 args.getProperties().setProperty("jetty.base",this.baseDir.toString(),"<internal-fallback>");
194             }
195             else
196             {
197                 this.baseDir = basePath.getAbsoluteFile();
198             }
199         }
200         else if (basePath != null)
201         {
202             // logic if home is undeclared
203             this.baseDir = basePath.getAbsoluteFile();
204         }
205 
206         // Update System Properties
207         args.addSystemProperty("jetty.home",this.homeDir.getAbsolutePath());
208         args.addSystemProperty("jetty.base",this.baseDir.getAbsolutePath());
209     }
210 
211     public boolean isBaseDifferent()
212     {
213         return homeDir.compareTo(baseDir) != 0;
214     }
215 
216     /**
217      * Get all of the files that are in a specific relative directory.
218      * <p>
219      * If the same found path exists in both <code>${jetty.base}</code> and <code>${jetty.home}</code>, then the one in <code>${jetty.base}</code> is returned
220      * (it overrides the one in ${jetty.home})
221      * 
222      * @param relPathToDirectory
223      *            the relative path to the directory
224      * @return the list of files found.
225      */
226     public List<File> listFiles(String relPathToDirectory)
227     {
228         return listFiles(relPathToDirectory,FS.AllFilter.INSTANCE);
229     }
230 
231     /**
232      * Get all of the files that are in a specific relative directory, with applied {@link FileFilter}
233      * <p>
234      * If the same found path exists in both <code>${jetty.base}</code> and <code>${jetty.home}</code>, then the one in <code>${jetty.base}</code> is returned
235      * (it overrides the one in ${jetty.home})
236      * 
237      * @param relPathToDirectory
238      *            the relative path to the directory
239      * @param filter
240      *            the filter to use
241      * @return the list of files found.
242      */
243     public List<File> listFiles(String relPathToDirectory, FileFilter filter)
244     {
245         Objects.requireNonNull(filter,"FileFilter cannot be null");
246 
247         File homePath = new File(homeDir,FS.separators(relPathToDirectory));
248         List<File> homeFiles = new ArrayList<>();
249         if (FS.canReadDirectory(homePath))
250         {
251             homeFiles.addAll(Arrays.asList(homePath.listFiles(filter)));
252         }
253 
254         if (isBaseDifferent())
255         {
256             // merge
257             File basePath = new File(baseDir,FS.separators(relPathToDirectory));
258             List<File> ret = new ArrayList<>();
259             if (FS.canReadDirectory(basePath))
260             {
261                 File baseFiles[] = basePath.listFiles(filter);
262 
263                 if (baseFiles != null)
264                 {
265                     for (File base : baseFiles)
266                     {
267                         String relpath = toRelativePath(baseDir,base);
268                         File home = new File(homeDir,FS.separators(relpath));
269                         if (home.exists())
270                         {
271                             homeFiles.remove(home);
272                         }
273                         ret.add(base);
274                     }
275                 }
276             }
277 
278             // add any remaining home files.
279             ret.addAll(homeFiles);
280 
281             Collections.sort(ret,new NaturalSort.Files());
282             return ret;
283         }
284         else
285         {
286             // simple return
287             Collections.sort(homeFiles,new NaturalSort.Files());
288             return homeFiles;
289         }
290     }
291     
292     /**
293      * Get all of the files that are in a specific relative directory, with applied regex.
294      * <p>
295      * If the same found path exists in both <code>${jetty.base}</code> and <code>${jetty.home}</code>, then the one in <code>${jetty.base}</code> is returned
296      * (it overrides the one in ${jetty.home})
297      * <p>
298      * All regex paths are assumed to be in unix notation (use of <code>"/"</code> to separate paths, as <code>"\"</code> is used to escape in regex)
299      * 
300      * @param regex
301      *            the regex to use to match against the found files.
302      * @return the list of files found.
303      */
304     public List<File> listFilesRegex(String regex)
305     {
306         Objects.requireNonNull(regex,"Glob cannot be null");
307 
308         Pattern pattern = Pattern.compile(regex);
309 
310         List<File> homeFiles = new ArrayList<>();
311         if (FS.canReadDirectory(homeDir))
312         {
313             StartLog.debug("Finding files in ${jetty.home} that match: %s",regex);
314             recurseDir(homeFiles,homeDir,new FS.RelativeRegexFilter(homeDir,pattern));
315             StartLog.debug("Found %,d files",homeFiles.size());
316         }
317 
318         if (isBaseDifferent())
319         {
320             // merge
321             List<File> ret = new ArrayList<>();
322             if (FS.canReadDirectory(baseDir))
323             {
324                 List<File> baseFiles = new ArrayList<>();
325                 StartLog.debug("Finding files in ${jetty.base} that match: %s",regex);
326                 recurseDir(baseFiles,baseDir,new FS.RelativeRegexFilter(baseDir,pattern));
327                 StartLog.debug("Found %,d files",baseFiles.size());
328 
329                 for (File base : baseFiles)
330                 {
331                     String relpath = toRelativePath(baseDir,base);
332                     File home = new File(homeDir,FS.separators(relpath));
333                     if (home.exists())
334                     {
335                         homeFiles.remove(home);
336                     }
337                     ret.add(base);
338                 }
339             }
340 
341             // add any remaining home files.
342             ret.addAll(homeFiles);
343             StartLog.debug("Merged Files: %,d files%n",ret.size());
344 
345             Collections.sort(ret,new NaturalSort.Files());
346             return ret;
347         }
348         else
349         {
350             // simple return
351             Collections.sort(homeFiles,new NaturalSort.Files());
352             return homeFiles;
353         }
354     }
355 
356     private void recurseDir(List<File> files, File dir, RelativeRegexFilter filter)
357     {
358         // find matches first
359         files.addAll(Arrays.asList(dir.listFiles(filter)));
360 
361         // now dive down into sub-directories
362         for (File subdir : dir.listFiles(FS.DirFilter.INSTANCE))
363         {
364             recurseDir(files,subdir,filter);
365         }
366     }
367 
368     /**
369      * Collect the list of files in both <code>${jetty.base}</code> and <code>${jetty.home}</code>, even if the same file shows up in both places.
370      */
371     public List<File> rawListFiles(String relPathToDirectory, FileFilter filter)
372     {
373         Objects.requireNonNull(filter,"FileFilter cannot be null");
374 
375         List<File> ret = new ArrayList<>();
376 
377         // Home Dir
378         File homePath = new File(homeDir,FS.separators(relPathToDirectory));
379         ret.addAll(Arrays.asList(homePath.listFiles(filter)));
380 
381         if (isBaseDifferent())
382         {
383             // Base Dir
384             File basePath = new File(baseDir,FS.separators(relPathToDirectory));
385             ret.addAll(Arrays.asList(basePath.listFiles(filter)));
386         }
387 
388         // Sort
389         Collections.sort(ret,new NaturalSort.Files());
390         return ret;
391     }
392 
393     public void setBaseDir(File dir)
394     {
395         try
396         {
397             this.baseDir = dir.getCanonicalFile();
398             System.setProperty("jetty.base",dir.getCanonicalPath());
399         }
400         catch (IOException e)
401         {
402             e.printStackTrace(System.err);
403         }
404     }
405 
406     public void setHomeDir(File dir)
407     {
408         try
409         {
410             this.homeDir = dir.getCanonicalFile();
411             System.setProperty("jetty.home",dir.getCanonicalPath());
412         }
413         catch (IOException e)
414         {
415             e.printStackTrace(System.err);
416         }
417     }
418 
419     // TODO - inline
420     private String toRelativePath(File dir, File path)
421     {
422         return FS.toRelativePath(dir,path);
423     }
424 
425     /**
426      * Convenience method for <code>toShortForm(file.getCanonicalPath())</code>
427      */
428     public String toShortForm(File path)
429     {
430         try
431         {
432             return toShortForm(path.getCanonicalPath());
433         }
434         catch (IOException ignore)
435         {
436             /* ignore */
437         }
438         return toShortForm(path.getAbsolutePath());
439     }
440 
441     /**
442      * Replace/Shorten arbitrary path with property strings <code>"${jetty.home}"</code> or <code>"${jetty.base}"</code> where appropriate.
443      * 
444      * @param path
445      *            the path to shorten
446      * @return the potentially shortened path
447      */
448     public String toShortForm(String path)
449     {
450         if (path == null)
451         {
452             return path;
453         }
454 
455         String value;
456 
457         if (isBaseDifferent())
458         {
459             value = baseDir.getAbsolutePath();
460             if (path.startsWith(value))
461             {
462                 return "${jetty.base}" + path.substring(value.length());
463             }
464         }
465 
466         value = homeDir.getAbsolutePath();
467 
468         if (path.startsWith(value))
469         {
470             return "${jetty.home}" + path.substring(value.length());
471         }
472 
473         return path;
474     }
475 
476 }