View Javadoc

1   package org.lsst.ccs.bootstrap;
2   
3   import java.io.File;
4   import java.net.URISyntaxException;
5   import java.net.URL;
6   import java.nio.file.Path;
7   import java.nio.file.Paths;
8   import java.util.ArrayList;
9   import java.util.List;
10  import java.util.Properties;
11  import java.util.StringTokenizer;
12  import java.util.regex.Matcher;
13  import java.util.regex.Pattern;
14  import org.lsst.ccs.bootstrap.resources.BootstrapResourceUtils;
15  import org.lsst.ccs.bootstrap.resources.ResourcesTree;
16  import org.lsst.ccs.bootstrap.resources.ResourcesUtils;
17  
18  /**
19   * Utility methods for the Bootstrap application.
20   *
21   * @author turri
22   */
23  public abstract class BootstrapUtils {
24  
25      public static final String FILE_SEPARATOR = System.getProperty("file.separator");
26      public static final String PATH_SEPARATOR = System.getProperty("path.separator");
27      protected static final String DISTRIBUTION_JARS_DIRECTORY = "share" + FILE_SEPARATOR + "java" + FILE_SEPARATOR;
28      private static final String DISTRIBUTION_RESOURCES_DIRECTORY = "etc" + FILE_SEPARATOR;
29      private static final String DISTRIBUTION_LIB_DIRECTORY = "lib" + FILE_SEPARATOR;
30      private static final String DISTRIBUTION_JNI_DIRECTORY = DISTRIBUTION_LIB_DIRECTORY + "jni" + FILE_SEPARATOR;
31      protected static final String APPLICATION_MAINCLASS_PROPERTY = "org.lsst.ccs.application.mainClass";
32      protected static final String OBSOLETE_MAINJAR_PROPERTY = "org.lsst.ccs.application.mainJar";
33      private static List<String> listOfApplications = null;
34  
35      // Pattern to substitute environment variable in user provided properties
36      private static final String envVar_str_pattern = ".*(\\$\\[env\\.(.*)\\]).*";
37      private static final Pattern envVar_pattern = Pattern.compile(envVar_str_pattern);
38  
39      /**
40       * Environment variable that defines the resource path.
41       */
42      private final static String CCS_RESOURCE_PATH_ENV_VAR = "CCS_RESOURCE_PATH";
43      private final static String CCS_DISTRIBUTION_PATH_ENV_VAR = "CCS_DISTRIBUTION_PATH";
44      private static String ccsDistributionRootDirectory = null;
45      private static ResourcesTree resourcesTree = null;
46  
47      protected static void reset() {
48          resourcesTree = null;
49          ccsDistributionRootDirectory = null;
50          listOfApplications = null;
51      }
52      
53      /**
54       * Get the ResourceTree for this Bootstrap environment. It contains all the
55       * resources defined in the resource directory list.
56       *
57       * @return
58       */
59      public synchronized static ResourcesTree getBootstrapResourcesTree() {
60          if (resourcesTree == null) {
61              resourcesTree = new ResourcesTree();
62              List<String> resourceDirectories = getOrderedListOfResourceDirectories();
63              List<String> additionalResourceDirectories = extractDirectoriesFromPath(getUserProvidedResourceDirectories(), null, true);
64  
65              for (String resourceDirectory : resourceDirectories) {
66                  File resourceDir = new File(resourceDirectory);
67                  if (resourceDir.exists()) {
68                      if ( additionalResourceDirectories.contains(resourceDirectory) ) {
69                          resourcesTree.addUserResourceDirectory(resourceDirectory);
70                      } else {
71                          resourcesTree.addDistributionResourceDirectory(resourceDirectory);
72                      }
73                  } else {
74                      if (Bootstrap.isBootstrapEnvironment()) {
75                          System.out.println("***** WARNING: resource directory " + resourceDirectory + " does not exist.");
76                      }
77                  }
78              }
79          }
80          return resourcesTree;
81      }
82  
83      protected static String getUserProvidedResourceDirectories() {
84          String userProvidedResourceDirectories = null;
85          if ( "true".equals(System.getProperty("org.lsst.ccs.bootstrap.test"))  ) {
86              userProvidedResourceDirectories = System.getProperty("org.lsst."+CCS_RESOURCE_PATH_ENV_VAR.replace("_", ".").toLowerCase());
87              if ( userProvidedResourceDirectories != null ) {
88                  return userProvidedResourceDirectories;
89              }
90          }
91          return System.getenv(CCS_RESOURCE_PATH_ENV_VAR);        
92      }
93      
94      protected static String getUserProvidedDistributionDirectories() {
95          String userProvidedDistributionDirectories = null;
96          if ( "true".equals(System.getProperty("org.lsst.ccs.bootstrap.test") ) ) {
97              userProvidedDistributionDirectories = System.getProperty("org.lsst."+CCS_DISTRIBUTION_PATH_ENV_VAR.replace("_", ".").toLowerCase());
98              if ( userProvidedDistributionDirectories != null ) {
99                  return userProvidedDistributionDirectories;
100             }
101         }
102         return System.getenv(CCS_DISTRIBUTION_PATH_ENV_VAR);                
103     }
104     
105     /**
106      * Get the ordered list of resource directories.
107      *
108      * The built in default is: - Distribution resources directory:
109      * distribution/etc/ - System resources directory: /etc/ccs/ - User
110      * resources directory: ~/.ccs/etc/
111      *
112      * By setting the environment variable CCS_RESOURCE_PATH, the developer can
113      * change which resource directories are searched and in which order.
114      *
115      * When the CCS_RESOURCE_PATH environment variable is set, the distribution
116      * resource directory will be still searched first, before any of the
117      * directories provided by the user.
118      *
119      * This list is built once and its result is cached.
120      *
121      * @return The list of ordered directories used to search for resources.
122      *
123      */
124     static List<String> getOrderedListOfResourceDirectories() {
125         String userProvidedResourceDirs = getUserProvidedResourceDirectories();
126         String userProvidedDistributionDirs = getUserProvidedDistributionDirectories();
127         return getOrderedListOfResourceDirectories(userProvidedResourceDirs, userProvidedDistributionDirs, true);
128     }
129 
130     /**
131      * This method should do all the work of setting up the resource directories
132      * as it is used in by the tests.
133      *
134      * @param userProvidedResourceDirs This resembles the environment variable
135      * to set the list of resource directories.
136      * @param userProvidedDistributions This resembles the environment variable
137      * to set the list of additional distributions.
138      * @return The list of ordered directories to search for resources.
139      *
140      */
141     static List<String> getOrderedListOfResourceDirectories(String userProvidedResourceDirs, String userProvidedDistributions, boolean checkExistance) {
142 
143         String distributionResourceDirectoriy = getDistributionResourcesDirectory();
144 
145         List<String> additionaDistributionResourceDirectories = extractDirectoriesFromPath(userProvidedDistributions, DISTRIBUTION_RESOURCES_DIRECTORY, checkExistance);
146 
147         List<String> additionalResourceDirectories = extractDirectoriesFromPath(userProvidedResourceDirs, null, checkExistance);
148 
149         List<String> orderedListOfResourceDirectories = new ArrayList<>();
150 
151         // First add the additional resource directories
152         for (String dir : additionalResourceDirectories) {
153             orderedListOfResourceDirectories.add(dir);
154         }
155 
156         // Then add the main distribution directory
157         orderedListOfResourceDirectories.add(distributionResourceDirectoriy);
158 
159         // Finally add any additional distribution directory
160         for (String dir : additionaDistributionResourceDirectories) {
161             orderedListOfResourceDirectories.add(dir);
162         }
163 
164         return orderedListOfResourceDirectories;
165     }
166 
167     protected static List<String> extractDirectoriesFromPath(String directoryPath, String appendDirectory, boolean checkExistance) {
168 
169         ArrayList<String> extractedDirectories = new ArrayList<>();
170 
171         if (directoryPath != null && !"".equals(directoryPath)) {
172             StringTokenizer dirsToken = new StringTokenizer(directoryPath, PATH_SEPARATOR);
173             while (dirsToken.hasMoreTokens()) {
174                 String resourceDir = dirsToken.nextToken().trim();
175                 if (!resourceDir.endsWith(FILE_SEPARATOR)) {
176                     resourceDir += FILE_SEPARATOR;
177                 }
178                 if (appendDirectory != null && !"".equals(appendDirectory)) {
179                     if (!appendDirectory.endsWith(FILE_SEPARATOR)) {
180                         appendDirectory += FILE_SEPARATOR;
181                     }
182                     resourceDir += appendDirectory;
183                 }
184 
185                 //The root for relative paths is the root of the CCS distribution.
186                 Path p = Paths.get(BootstrapUtils.getCCSDistributionRootDirectory());
187                 resourceDir = resourceDir.replace("~", System.getProperty("user.home"));
188                 resourceDir = p.resolve(resourceDir).normalize().toString();
189                 if (!resourceDir.endsWith(FILE_SEPARATOR)) {
190                     resourceDir += FILE_SEPARATOR;
191                 }
192                 File dir = new File(resourceDir);
193                 if (checkExistance && !dir.exists()) {
194                     if (Bootstrap.isBootstrapEnvironment()) {
195                         System.out.println("** Ignoring resource directory " + resourceDir + " as it does not exist.");
196                         System.out.println("** Please update the environment variable " + CCS_RESOURCE_PATH_ENV_VAR + " or " + CCS_DISTRIBUTION_PATH_ENV_VAR);
197                     }
198                 } else {
199                     extractedDirectories.add(resourceDir);
200                 }
201             }
202         }
203         return extractedDirectories;
204     }
205 
206     /**
207      * Get the Distribution resources directory.
208      *
209      * @return The resources directory in the distribution.
210      */
211     public static String getDistributionResourcesDirectory() {
212         return getDistributionResourcesDirectory(getCCSDistributionRootDirectory());
213     }
214 
215     static String getDistributionResourcesDirectory(String distribution) {
216         if (!distribution.endsWith(FILE_SEPARATOR)) {
217             distribution += FILE_SEPARATOR;
218         }
219         return distribution + DISTRIBUTION_RESOURCES_DIRECTORY;
220     }
221 
222     /**
223      * Get the lib directory in the distribution.
224      *
225      * @return the lib files directory.
226      *
227      */
228     static String getDistributionLibDirectory() {
229         return getDistributionLibDirectory(getCCSDistributionRootDirectory());
230     }
231 
232     static String getDistributionLibDirectory(String distribution) {
233         if (!distribution.endsWith(FILE_SEPARATOR)) {
234             distribution += FILE_SEPARATOR;
235         }
236         return distribution + DISTRIBUTION_LIB_DIRECTORY;
237     }
238 
239     /**
240      * Get the jni directory in the distribution.
241      *
242      * @return the jni files directory.
243      *
244      */
245     static String getDistributionJniDirectory() {
246         return getDistributionJniDirectory(getCCSDistributionRootDirectory());
247     }
248 
249     static String getDistributionJniDirectory(String distribution) {
250         if (!distribution.endsWith(FILE_SEPARATOR)) {
251             distribution += FILE_SEPARATOR;
252         }
253         return distribution + DISTRIBUTION_JNI_DIRECTORY;
254     }
255 
256     /**
257      * Get the jar files directory in the distribution.
258      *
259      * @return the jar files directory.
260      *
261      */
262     static String getDistributionJarFilesDirectory() {
263         return getDistributionJarFilesDirectory(getCCSDistributionRootDirectory());
264     }
265 
266     static String getDistributionJarFilesDirectory(String distribution) {
267         if (!distribution.endsWith(FILE_SEPARATOR)) {
268             distribution += FILE_SEPARATOR;
269         }
270         return distribution + DISTRIBUTION_JARS_DIRECTORY;
271     }
272 
273     /**
274      * Get the root directory of the CCS distribution from which the Bootstrap
275      * code has been launched.
276      *
277      * @return The root directory of the CCS distribution. It ends with a
278      * file.separator.
279      */
280     public static String getCCSDistributionRootDirectory() {
281         if (ccsDistributionRootDirectory == null) {
282             ccsDistributionRootDirectory = getCCSDistributionRootDirectory(Bootstrap.getLoaderClass());
283         }
284         return ccsDistributionRootDirectory;
285     }
286 
287     /**
288      * This is to be used only in test environment.
289      *
290      * @param clazz
291      * @return
292      */
293     private static String getCCSDistributionRootDirectory(Class clazz) {
294         URL location = clazz.getProtectionDomain().getCodeSource().getLocation();
295         try {
296             Path path = Paths.get(location.toURI());
297             String sourceCodeLocation = path.toString();
298             if (sourceCodeLocation.endsWith(".jar")) {
299                 ccsDistributionRootDirectory = sourceCodeLocation.substring(0, sourceCodeLocation.lastIndexOf(FILE_SEPARATOR) + 1);
300             } else if (path.toFile().isDirectory()) {
301                 ccsDistributionRootDirectory = sourceCodeLocation + FILE_SEPARATOR;
302             } else {
303                 throw new RuntimeException("Could not process souce code location " + sourceCodeLocation + " It is neither a directory nor a jar file.");
304             }
305         } catch (URISyntaxException x) {
306             throw new RuntimeException("Could not process souce code location ", x);
307         }
308 
309         if (ccsDistributionRootDirectory.endsWith(DISTRIBUTION_JARS_DIRECTORY)) {
310             ccsDistributionRootDirectory = ccsDistributionRootDirectory.replace(DISTRIBUTION_JARS_DIRECTORY, "");
311 //            throw new RuntimeException("This executable is not being run from a CCS distribution directory. "+ccsDistributionRootDirectory);
312         }
313         return ccsDistributionRootDirectory;
314     }
315 
316     /**
317      * Get the list of available applications for the current Bootstrap
318      * configuration.
319      *
320      * @return The list of application names.
321      *
322      */
323     public synchronized static List<String> getBootstrapListOfApplications() {
324         if (listOfApplications == null) {
325             listOfApplications = new ArrayList<>();
326             List<String> allPropertiesInResources = ResourcesUtils.getResourcesInResourcesTreeByExtension(getBootstrapResourcesTree(), "properties", 0);
327             for (String propertyFile : allPropertiesInResources) {
328                 Properties props = ResourcesUtils.getMergedPropertyFile(getBootstrapResourcesTree(), propertyFile);
329                 String mainClass = props.getProperty(APPLICATION_MAINCLASS_PROPERTY);
330                 if (mainClass != null && !mainClass.isEmpty()) {
331                     listOfApplications.add(propertyFile.replace(".properties", ""));
332                 }
333             }
334         }
335         return listOfApplications;
336     }
337 
338     /**
339      * Get the application definition properties file for a given application
340      * name.
341      *
342      * @param application The name of the application.
343      * @return The Properties that define the application.
344      */
345     static Properties getApplicationDefinitionProperties(String application) {
346 //        return getMergedProperties(application);
347         if ( ! application.endsWith(".properties") ) {
348             application = application += ".properties";
349         }        
350         return BootstrapResourceUtils.getBootstrapProperties(application);
351     }
352 
353     static String getBootstrapLibraryPath() {
354         String libraryPath = BootstrapUtils.getDistributionJniDirectory() + File.pathSeparator + BootstrapUtils.getDistributionLibDirectory();
355         String existingLibraryPath = System.getenv("LD_LIBRARY_PATH");
356         if (existingLibraryPath != null && !existingLibraryPath.isEmpty()) {
357             libraryPath += File.pathSeparator + existingLibraryPath;
358         }
359         return libraryPath;
360     }
361 
362     /**
363      * Parse a user provided property to match environment variables specified
364      * in the form ${ENV_VARIABLE}
365      *
366      */
367     public static String parseProperty(String inputProperty) {
368         String outProperty = inputProperty;
369         Matcher m = envVar_pattern.matcher(outProperty);
370         if (m.matches()) {
371             String envVarValue = System.getenv(m.group(2));
372             if (envVarValue != null) {
373                 outProperty = outProperty.replace(m.group(1), envVarValue);
374             } else {
375                 if (Bootstrap.isBootstrapEnvironment()) {
376                     System.out.println("[WARNING] Environment variable " + m.group(2) + " is not defined.");
377                 }
378             }
379         }
380         return outProperty;
381     }
382 
383 }