View Javadoc
1   /*
2    * This file is part of dependency-check-core.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Copyright (c) 2012 Jeremy Long. All Rights Reserved.
17   */
18  package org.owasp.dependencycheck.utils;
19  
20  import org.slf4j.Logger;
21  import org.slf4j.LoggerFactory;
22  
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.FileNotFoundException;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.io.PrintWriter;
29  import java.io.StringWriter;
30  import java.io.UnsupportedEncodingException;
31  import java.net.URLDecoder;
32  import java.util.Enumeration;
33  import java.util.Properties;
34  
35  /**
36   * A simple settings container that wraps the dependencycheck.properties file.
37   *
38   * @author Jeremy Long
39   */
40  public final class Settings {
41  
42      //<editor-fold defaultstate="collapsed" desc="KEYS used to access settings">
43      /**
44       * The collection of keys used within the properties file.
45       */
46      public static final class KEYS {
47  
48          /**
49           * private constructor because this is a "utility" class containing constants
50           */
51          private KEYS() {
52              //do nothing
53          }
54          /**
55           * The key to obtain the application name.
56           */
57          public static final String APPLICATION_VAME = "application.name";
58          /**
59           * The key to obtain the application version.
60           */
61          public static final String APPLICATION_VERSION = "application.version";
62          /**
63           * The key to obtain the URL to retrieve the current release version from.
64           */
65          public static final String ENGINE_VERSION_CHECK_URL = "engine.version.url";
66          /**
67           * The properties key indicating whether or not the cached data sources should be updated.
68           */
69          public static final String AUTO_UPDATE = "autoupdate";
70          /**
71           * The database driver class name. If this is not in the properties file the embedded database is used.
72           */
73          public static final String DB_DRIVER_NAME = "data.driver_name";
74          /**
75           * The database driver class name. If this is not in the properties file the embedded database is used.
76           */
77          public static final String DB_DRIVER_PATH = "data.driver_path";
78          /**
79           * The database connection string. If this is not in the properties file the embedded database is used.
80           */
81          public static final String DB_CONNECTION_STRING = "data.connection_string";
82          /**
83           * The username to use when connecting to the database.
84           */
85          public static final String DB_USER = "data.user";
86          /**
87           * The password to authenticate to the database.
88           */
89          public static final String DB_PASSWORD = "data.password";
90          /**
91           * The base path to use for the data directory (for embedded db).
92           */
93          public static final String DATA_DIRECTORY = "data.directory";
94          /**
95           * The database file name.
96           */
97          public static final String DB_FILE_NAME = "data.file_name";
98          /**
99           * The database schema version.
100          */
101         public static final String DB_VERSION = "data.version";
102         /**
103          * The properties key for the URL to retrieve the "meta" data from about the CVE entries.
104          */
105         public static final String CVE_META_URL = "cve.url.meta";
106         /**
107          * The properties key for the URL to retrieve the recently modified and added CVE entries (last 8 days) using the 2.0
108          * schema.
109          */
110         public static final String CVE_MODIFIED_20_URL = "cve.url-2.0.modified";
111         /**
112          * The properties key for the URL to retrieve the recently modified and added CVE entries (last 8 days) using the 1.2
113          * schema.
114          */
115         public static final String CVE_MODIFIED_12_URL = "cve.url-1.2.modified";
116         /**
117          * The properties key for the URL to retrieve the recently modified and added CVE entries (last 8 days).
118          */
119         public static final String CVE_MODIFIED_VALID_FOR_DAYS = "cve.url.modified.validfordays";
120         /**
121          * The properties key to control the skipping of the check for CVE updates.
122          */
123         public static final String CVE_CHECK_VALID_FOR_HOURS = "cve.check.validforhours";
124         /**
125          * The properties key for the telling us how many cve.url.* URLs exists. This is used in combination with CVE_BASE_URL to
126          * be able to retrieve the URLs for all of the files that make up the NVD CVE listing.
127          */
128         public static final String CVE_START_YEAR = "cve.startyear";
129         /**
130          * The properties key for the CVE schema version 1.2.
131          */
132         public static final String CVE_SCHEMA_1_2 = "cve.url-1.2.base";
133         /**
134          * The properties key for the CVE schema version 2.0.
135          */
136         public static final String CVE_SCHEMA_2_0 = "cve.url-2.0.base";
137         /**
138          * The properties key that indicates how often the CPE data needs to be updated.
139          */
140         public static final String CPE_MODIFIED_VALID_FOR_DAYS = "cpe.validfordays";
141         /**
142          * The properties key for the URL to retrieve the CPE.
143          */
144         public static final String CPE_URL = "cpe.url";
145         /**
146          * The properties key for the proxy server.
147          *
148          * @deprecated use {@link org.owasp.dependencycheck.utils.Settings.KEYS#PROXY_SERVER} instead.
149          */
150         @Deprecated
151         public static final String PROXY_URL = "proxy.server";
152         /**
153          * The properties key for the proxy server.
154          */
155         public static final String PROXY_SERVER = "proxy.server";
156         /**
157          * The properties key for the proxy port - this must be an integer value.
158          */
159         public static final String PROXY_PORT = "proxy.port";
160         /**
161          * The properties key for the proxy username.
162          */
163         public static final String PROXY_USERNAME = "proxy.username";
164         /**
165          * The properties key for the proxy password.
166          */
167         public static final String PROXY_PASSWORD = "proxy.password";
168         /**
169          * The properties key for the non proxy hosts.
170          */
171         public static final String PROXY_NON_PROXY_HOSTS = "proxy.nonproxyhosts";
172         /**
173          * The properties key for the connection timeout.
174          */
175         public static final String CONNECTION_TIMEOUT = "connection.timeout";
176         /**
177          * The location of the temporary directory.
178          */
179         public static final String TEMP_DIRECTORY = "temp.directory";
180         /**
181          * The maximum number of threads to allocate when downloading files.
182          */
183         public static final String MAX_DOWNLOAD_THREAD_POOL_SIZE = "max.download.threads";
184         /**
185          * The key for a list of suppression files.
186          */
187         public static final String SUPPRESSION_FILE = "suppression.file";
188         /**
189          * The properties key for whether the Jar Analyzer is enabled.
190          */
191         public static final String ANALYZER_JAR_ENABLED = "analyzer.jar.enabled";
192         /**
193          * The properties key for whether experimental analyzers are loaded.
194          */
195         public static final String ANALYZER_EXPERIMENTAL_ENABLED = "analyzer.experimental.enabled";
196         /**
197          * The properties key for whether the Archive analyzer is enabled.
198          */
199         public static final String ANALYZER_ARCHIVE_ENABLED = "analyzer.archive.enabled";
200         /**
201          * The properties key for whether the node.js package analyzer is enabled.
202          */
203         public static final String ANALYZER_NODE_PACKAGE_ENABLED = "analyzer.node.package.enabled";
204         /**
205          * The properties key for whether the composer lock file analyzer is enabled.
206          */
207         public static final String ANALYZER_COMPOSER_LOCK_ENABLED = "analyzer.composer.lock.enabled";
208         /**
209          * The properties key for whether the Python Distribution analyzer is enabled.
210          */
211         public static final String ANALYZER_PYTHON_DISTRIBUTION_ENABLED = "analyzer.python.distribution.enabled";
212         /**
213          * The properties key for whether the Python Package analyzer is enabled.
214          */
215         public static final String ANALYZER_PYTHON_PACKAGE_ENABLED = "analyzer.python.package.enabled";
216         /**
217          * The properties key for whether the Ruby Gemspec Analyzer is enabled.
218          */
219         public static final String ANALYZER_RUBY_GEMSPEC_ENABLED = "analyzer.ruby.gemspec.enabled";
220         /**
221          * The properties key for whether the Autoconf analyzer is enabled.
222          */
223         public static final String ANALYZER_AUTOCONF_ENABLED = "analyzer.autoconf.enabled";
224         /**
225          * The properties key for whether the CMake analyzer is enabled.
226          */
227         public static final String ANALYZER_CMAKE_ENABLED = "analyzer.cmake.enabled";
228         /**
229          * The properties key for whether the Ruby Bundler Audit analyzer is enabled.
230          */
231         public static final String ANALYZER_BUNDLE_AUDIT_ENABLED = "analyzer.bundle.audit.enabled";
232         /**
233          * The properties key for whether the .NET Assembly analyzer is enabled.
234          */
235         public static final String ANALYZER_ASSEMBLY_ENABLED = "analyzer.assembly.enabled";
236         /**
237          * The properties key for whether the .NET Nuspec analyzer is enabled.
238          */
239         public static final String ANALYZER_NUSPEC_ENABLED = "analyzer.nuspec.enabled";
240         /**
241          * The properties key for whether the Nexus analyzer is enabled.
242          */
243         public static final String ANALYZER_NEXUS_ENABLED = "analyzer.nexus.enabled";
244         /**
245          * The properties key for the Nexus search URL.
246          */
247         public static final String ANALYZER_NEXUS_URL = "analyzer.nexus.url";
248         /**
249          * The properties key for using the proxy to reach Nexus.
250          */
251         public static final String ANALYZER_NEXUS_USES_PROXY = "analyzer.nexus.proxy";
252         /**
253          * The properties key for whether the Central analyzer is enabled.
254          */
255         public static final String ANALYZER_CENTRAL_ENABLED = "analyzer.central.enabled";
256         /**
257          * The properties key for whether the OpenSSL analyzer is enabled.
258          */
259         public static final String ANALYZER_OPENSSL_ENABLED = "analyzer.openssl.enabled";
260         /**
261          * The properties key for the Central search URL.
262          */
263         public static final String ANALYZER_CENTRAL_URL = "analyzer.central.url";
264         /**
265          * The path to mono, if available.
266          */
267         public static final String ANALYZER_ASSEMBLY_MONO_PATH = "analyzer.assembly.mono.path";
268         /**
269          * The path to bundle-audit, if available.
270          */
271         public static final String ANALYZER_BUNDLE_AUDIT_PATH = "analyzer.bundle.audit.path";
272         /**
273          * The additional configured zip file extensions, if available.
274          */
275         public static final String ADDITIONAL_ZIP_EXTENSIONS = "extensions.zip";
276         /**
277          * The key to obtain the path to the VFEED data file.
278          */
279         public static final String VFEED_DATA_FILE = "vfeed.data_file";
280         /**
281          * The key to obtain the VFEED connection string.
282          */
283         public static final String VFEED_CONNECTION_STRING = "vfeed.connection_string";
284 
285         /**
286          * The key to obtain the base download URL for the VFeed data file.
287          */
288         public static final String VFEED_DOWNLOAD_URL = "vfeed.download_url";
289         /**
290          * The key to obtain the download file name for the VFeed data.
291          */
292         public static final String VFEED_DOWNLOAD_FILE = "vfeed.download_file";
293         /**
294          * The key to obtain the VFeed update status.
295          */
296         public static final String VFEED_UPDATE_STATUS = "vfeed.update_status";
297 
298         /**
299          * The HTTP request method for query last modified date.
300          */
301         public static final String DOWNLOADER_QUICK_QUERY_TIMESTAMP = "downloader.quick.query.timestamp";
302     }
303     //</editor-fold>
304 
305     /**
306      * The logger.
307      */
308     private static final Logger LOGGER = LoggerFactory.getLogger(Settings.class);
309     /**
310      * The properties file location.
311      */
312     private static final String PROPERTIES_FILE = "dependencycheck.properties";
313     /**
314      * Thread local settings.
315      */
316     private static final ThreadLocal<Settings> LOCAL_SETTINGS = new ThreadLocal<Settings>();
317     /**
318      * The properties.
319      */
320     private Properties props = null;
321 
322     /**
323      * Private constructor for the Settings class. This class loads the properties files.
324      *
325      * @param propertiesFilePath the path to the base properties file to load
326      */
327     private Settings(String propertiesFilePath) {
328         InputStream in = null;
329         props = new Properties();
330         try {
331             in = this.getClass().getClassLoader().getResourceAsStream(propertiesFilePath);
332             props.load(in);
333         } catch (IOException ex) {
334             LOGGER.error("Unable to load default settings.");
335             LOGGER.debug("", ex);
336         } finally {
337             if (in != null) {
338                 try {
339                     in.close();
340                 } catch (IOException ex) {
341                     LOGGER.trace("", ex);
342                 }
343             }
344         }
345         logProperties("Properties loaded", props);
346     }
347 
348     /**
349      * Initializes the thread local settings object. Note, to use the settings object you must call this method. However, you must
350      * also call Settings.cleanup() to properly release resources.
351      */
352     public static void initialize() {
353         LOCAL_SETTINGS.set(new Settings(PROPERTIES_FILE));
354     }
355 
356     /**
357      * Initializes the thread local settings object. Note, to use the settings object you must call this method. However, you must
358      * also call Settings.cleanup() to properly release resources.
359      *
360      * @param propertiesFilePath the path to the base properties file to load
361      */
362     public static void initialize(String propertiesFilePath) {
363         LOCAL_SETTINGS.set(new Settings(propertiesFilePath));
364     }
365 
366     /**
367      * Cleans up resources to prevent memory leaks.
368      *
369      */
370     public static void cleanup() {
371         cleanup(true);
372     }
373 
374     /**
375      * Cleans up resources to prevent memory leaks.
376      *
377      * @param deleteTemporary flag indicating whether any temporary directories generated should be removed
378      */
379     public static void cleanup(boolean deleteTemporary) {
380         if (deleteTemporary && tempDirectory != null && tempDirectory.exists()) {
381             FileUtils.delete(tempDirectory);
382             if (tempDirectory.exists()) {
383                 try {
384                     Thread.sleep(2000);
385                 } catch (InterruptedException ex) {
386                     LOGGER.trace("ignore", ex);
387                 }
388                 FileUtils.delete(tempDirectory);
389             }
390         }
391         try {
392             LOCAL_SETTINGS.remove();
393         } catch (Throwable ex) {
394             LOGGER.debug("Error cleaning up Settings", ex);
395         }
396     }
397 
398     /**
399      * Gets the underlying instance of the Settings object.
400      *
401      * @return the Settings object
402      */
403     public static Settings getInstance() {
404         return LOCAL_SETTINGS.get();
405     }
406 
407     /**
408      * Sets the instance of the Settings object to use in this thread.
409      *
410      * @param instance the instance of the settings object to use in this thread
411      */
412     public static void setInstance(Settings instance) {
413         LOCAL_SETTINGS.set(instance);
414     }
415 
416     /**
417      * Logs the properties. This will not log any properties that contain 'password' in the key.
418      *
419      * @param header the header to print with the log message
420      * @param properties the properties to log
421      */
422     private static void logProperties(String header, Properties properties) {
423         if (LOGGER.isDebugEnabled()) {
424             final StringWriter sw = new StringWriter();
425             PrintWriter pw = null;
426             try {
427                 pw = new PrintWriter(sw);
428                 pw.format("%s:%n%n", header);
429                 final Enumeration<?> e = properties.propertyNames();
430                 while (e.hasMoreElements()) {
431                     final String key = (String) e.nextElement();
432                     if (key.contains("password")) {
433                         pw.format("%s='*****'%n", key);
434                     } else {
435                         final String value = properties.getProperty(key);
436                         if (value != null) {
437                             pw.format("%s='%s'%n", key, value);
438                         }
439                     }
440                 }
441                 pw.flush();
442                 LOGGER.debug(sw.toString());
443             } finally {
444                 if (pw != null) {
445                     pw.close();
446                 }
447             }
448 
449         }
450     }
451 
452     /**
453      * Sets a property value.
454      *
455      * @param key the key for the property
456      * @param value the value for the property
457      */
458     public static void setString(String key, String value) {
459         LOCAL_SETTINGS.get().props.setProperty(key, value);
460         LOGGER.debug("Setting: {}='{}'", key, value);
461     }
462 
463     /**
464      * Sets a property value only if the value is not null.
465      *
466      * @param key the key for the property
467      * @param value the value for the property
468      */
469     public static void setStringIfNotNull(String key, String value) {
470         if (null != value) {
471             setString(key, value);
472         }
473     }
474 
475     /**
476      * Sets a property value only if the value is not null and not empty.
477      *
478      * @param key the key for the property
479      * @param value the value for the property
480      */
481     public static void setStringIfNotEmpty(String key, String value) {
482         if (null != value && !value.isEmpty()) {
483             setString(key, value);
484         }
485     }
486 
487     /**
488      * Sets a property value.
489      *
490      * @param key the key for the property
491      * @param value the value for the property
492      */
493     public static void setBoolean(String key, boolean value) {
494         setString(key, Boolean.toString(value));
495     }
496 
497     /**
498      * Sets a property value only if the value is not null.
499      *
500      * @param key the key for the property
501      * @param value the value for the property
502      */
503     public static void setBooleanIfNotNull(String key, Boolean value) {
504         if (null != value) {
505             setBoolean(key, value);
506         }
507     }
508 
509     /**
510      * Sets a property value.
511      *
512      * @param key the key for the property
513      * @param value the value for the property
514      */
515     public static void setInt(String key, int value) {
516         LOCAL_SETTINGS.get().props.setProperty(key, String.valueOf(value));
517         LOGGER.debug("Setting: {}='{}'", key, value);
518     }
519 
520     /**
521      * Sets a property value only if the value is not null.
522      *
523      * @param key the key for the property
524      * @param value the value for the property
525      */
526     public static void setIntIfNotNull(String key, Integer value) {
527         if (null != value) {
528             setInt(key, value);
529         }
530     }
531 
532     /**
533      * Merges a new properties file into the current properties. This method allows for the loading of a user provided properties
534      * file.<br><br>
535      * <b>Note</b>: even if using this method - system properties will be loaded before properties loaded from files.
536      *
537      * @param filePath the path to the properties file to merge.
538      * @throws FileNotFoundException is thrown when the filePath points to a non-existent file
539      * @throws IOException is thrown when there is an exception loading/merging the properties
540      */
541     public static void mergeProperties(File filePath) throws FileNotFoundException, IOException {
542         FileInputStream fis = null;
543         try {
544             fis = new FileInputStream(filePath);
545             mergeProperties(fis);
546         } finally {
547             if (fis != null) {
548                 try {
549                     fis.close();
550                 } catch (IOException ex) {
551                     LOGGER.trace("close error", ex);
552                 }
553             }
554         }
555     }
556 
557     /**
558      * Merges a new properties file into the current properties. This method allows for the loading of a user provided properties
559      * file.<br><br>
560      * Note: even if using this method - system properties will be loaded before properties loaded from files.
561      *
562      * @param filePath the path to the properties file to merge.
563      * @throws FileNotFoundException is thrown when the filePath points to a non-existent file
564      * @throws IOException is thrown when there is an exception loading/merging the properties
565      */
566     public static void mergeProperties(String filePath) throws FileNotFoundException, IOException {
567         FileInputStream fis = null;
568         try {
569             fis = new FileInputStream(filePath);
570             mergeProperties(fis);
571         } finally {
572             if (fis != null) {
573                 try {
574                     fis.close();
575                 } catch (IOException ex) {
576                     LOGGER.trace("close error", ex);
577                 }
578             }
579         }
580     }
581 
582     /**
583      * Merges a new properties file into the current properties. This method allows for the loading of a user provided properties
584      * file.<br><br>
585      * <b>Note</b>: even if using this method - system properties will be loaded before properties loaded from files.
586      *
587      * @param stream an Input Stream pointing at a properties file to merge
588      * @throws IOException is thrown when there is an exception loading/merging the properties
589      */
590     public static void mergeProperties(InputStream stream) throws IOException {
591         LOCAL_SETTINGS.get().props.load(stream);
592         logProperties("Properties updated via merge", LOCAL_SETTINGS.get().props);
593     }
594 
595     /**
596      * Returns a value from the properties file as a File object. If the value was specified as a system property or passed in via
597      * the -Dprop=value argument - this method will return the value from the system properties before the values in the contained
598      * configuration file.
599      *
600      * @param key the key to lookup within the properties file
601      * @return the property from the properties file converted to a File object
602      */
603     public static File getFile(String key) {
604         final String file = getString(key);
605         if (file == null) {
606             return null;
607         }
608         return new File(file);
609     }
610 
611     /**
612      * Returns a value from the properties file as a File object. If the value was specified as a system property or passed in via
613      * the -Dprop=value argument - this method will return the value from the system properties before the values in the contained
614      * configuration file.
615      *
616      * This method will check the configured base directory and will use this as the base of the file path. Additionally, if the
617      * base directory begins with a leading "[JAR]\" sequence with the path to the folder containing the JAR file containing this
618      * class.
619      *
620      * @param key the key to lookup within the properties file
621      * @return the property from the properties file converted to a File object
622      */
623     protected static File getDataFile(String key) {
624         final String file = getString(key);
625         LOGGER.debug("Settings.getDataFile() - file: '{}'", file);
626         if (file == null) {
627             return null;
628         }
629         if (file.startsWith("[JAR]")) {
630             LOGGER.debug("Settings.getDataFile() - transforming filename");
631             final File jarPath = getJarPath();
632             LOGGER.debug("Settings.getDataFile() - jar file: '{}'", jarPath.toString());
633             final File retVal = new File(jarPath, file.substring(6));
634             LOGGER.debug("Settings.getDataFile() - returning: '{}'", retVal.toString());
635             return retVal;
636         }
637         return new File(file);
638     }
639 
640     /**
641      * Attempts to retrieve the folder containing the Jar file containing the Settings class.
642      *
643      * @return a File object
644      */
645     private static File getJarPath() {
646         final String jarPath = Settings.class.getProtectionDomain().getCodeSource().getLocation().getPath();
647         String decodedPath = ".";
648         try {
649             decodedPath = URLDecoder.decode(jarPath, "UTF-8");
650         } catch (UnsupportedEncodingException ex) {
651             LOGGER.trace("", ex);
652         }
653 
654         final File path = new File(decodedPath);
655         if (path.getName().toLowerCase().endsWith(".jar")) {
656             return path.getParentFile();
657         } else {
658             return new File(".");
659         }
660     }
661 
662     /**
663      * Returns a value from the properties file. If the value was specified as a system property or passed in via the -Dprop=value
664      * argument - this method will return the value from the system properties before the values in the contained configuration
665      * file.
666      *
667      * @param key the key to lookup within the properties file
668      * @param defaultValue the default value for the requested property
669      * @return the property from the properties file
670      */
671     public static String getString(String key, String defaultValue) {
672         final String str = System.getProperty(key, LOCAL_SETTINGS.get().props.getProperty(key, defaultValue));
673         return str;
674     }
675 
676     /**
677      * A reference to the temporary directory; used incase it needs to be deleted during cleanup.
678      */
679     private static File tempDirectory = null;
680 
681     /**
682      * Returns the temporary directory.
683      *
684      * @return the temporary directory
685      * @throws java.io.IOException thrown if the temporary directory does not exist and cannot be created
686      */
687     public static File getTempDirectory() throws IOException {
688         final File tmpDir = new File(Settings.getString(Settings.KEYS.TEMP_DIRECTORY, System.getProperty("java.io.tmpdir")), "dctemp");
689         if (!tmpDir.exists() && !tmpDir.mkdirs()) {
690             final String msg = String.format("Unable to make a temporary folder '%s'", tmpDir.getPath());
691             throw new IOException(msg);
692         }
693         tempDirectory = tmpDir;
694         return tmpDir;
695     }
696 
697     /**
698      * Returns a value from the properties file. If the value was specified as a system property or passed in via the -Dprop=value
699      * argument - this method will return the value from the system properties before the values in the contained configuration
700      * file.
701      *
702      * @param key the key to lookup within the properties file
703      * @return the property from the properties file
704      */
705     public static String getString(String key) {
706         return System.getProperty(key, LOCAL_SETTINGS.get().props.getProperty(key));
707     }
708 
709     /**
710      * Removes a property from the local properties collection. This is mainly used in test cases.
711      *
712      * @param key the property key to remove
713      */
714     public static void removeProperty(String key) {
715         LOCAL_SETTINGS.get().props.remove(key);
716     }
717 
718     /**
719      * Returns an int value from the properties file. If the value was specified as a system property or passed in via the
720      * -Dprop=value argument - this method will return the value from the system properties before the values in the contained
721      * configuration file.
722      *
723      * @param key the key to lookup within the properties file
724      * @return the property from the properties file
725      * @throws InvalidSettingException is thrown if there is an error retrieving the setting
726      */
727     public static int getInt(String key) throws InvalidSettingException {
728         try {
729             return Integer.parseInt(Settings.getString(key));
730         } catch (NumberFormatException ex) {
731             throw new InvalidSettingException("Could not convert property '" + key + "' to an int.", ex);
732         }
733     }
734 
735     /**
736      * Returns an int value from the properties file. If the value was specified as a system property or passed in via the
737      * -Dprop=value argument - this method will return the value from the system properties before the values in the contained
738      * configuration file.
739      *
740      * @param key the key to lookup within the properties file
741      * @param defaultValue the default value to return
742      * @return the property from the properties file or the defaultValue if the property does not exist or cannot be converted to
743      * an integer
744      */
745     public static int getInt(String key, int defaultValue) {
746         int value;
747         try {
748             value = Integer.parseInt(Settings.getString(key));
749         } catch (NumberFormatException ex) {
750             if (!Settings.getString(key, "").isEmpty()) {
751                 LOGGER.debug("Could not convert property '{}={}' to an int; using {} instead.", key, Settings.getString(key), defaultValue);
752             }
753             value = defaultValue;
754         }
755         return value;
756     }
757 
758     /**
759      * Returns a long value from the properties file. If the value was specified as a system property or passed in via the
760      * -Dprop=value argument - this method will return the value from the system properties before the values in the contained
761      * configuration file.
762      *
763      * @param key the key to lookup within the properties file
764      * @return the property from the properties file
765      * @throws InvalidSettingException is thrown if there is an error retrieving the setting
766      */
767     public static long getLong(String key) throws InvalidSettingException {
768         try {
769             return Long.parseLong(Settings.getString(key));
770         } catch (NumberFormatException ex) {
771             throw new InvalidSettingException("Could not convert property '" + key + "' to a long.", ex);
772         }
773     }
774 
775     /**
776      * Returns a boolean value from the properties file. If the value was specified as a system property or passed in via the
777      * <code>-Dprop=value</code> argument this method will return the value from the system properties before the values in the
778      * contained configuration file.
779      *
780      * @param key the key to lookup within the properties file
781      * @return the property from the properties file
782      * @throws InvalidSettingException is thrown if there is an error retrieving the setting
783      */
784     public static boolean getBoolean(String key) throws InvalidSettingException {
785         return Boolean.parseBoolean(Settings.getString(key));
786     }
787 
788     /**
789      * Returns a boolean value from the properties file. If the value was specified as a system property or passed in via the
790      * <code>-Dprop=value</code> argument this method will return the value from the system properties before the values in the
791      * contained configuration file.
792      *
793      * @param key the key to lookup within the properties file
794      * @param defaultValue the default value to return if the setting does not exist
795      * @return the property from the properties file
796      * @throws InvalidSettingException is thrown if there is an error retrieving the setting
797      */
798     public static boolean getBoolean(String key, boolean defaultValue) throws InvalidSettingException {
799         return Boolean.parseBoolean(Settings.getString(key, Boolean.toString(defaultValue)));
800     }
801 
802     /**
803      * Returns a connection string from the configured properties. If the connection string contains a %s, this method will
804      * determine the 'data' directory and replace the %s with the path to the data directory. If the data directory does not
805      * exists it will be created.
806      *
807      * @param connectionStringKey the property file key for the connection string
808      * @param dbFileNameKey the settings key for the db filename
809      * @return the connection string
810      * @throws IOException thrown the data directory cannot be created
811      * @throws InvalidSettingException thrown if there is an invalid setting
812      */
813     public static String getConnectionString(String connectionStringKey, String dbFileNameKey)
814             throws IOException, InvalidSettingException {
815         final String connStr = Settings.getString(connectionStringKey);
816         if (connStr == null) {
817             final String msg = String.format("Invalid properties file; %s is missing.", connectionStringKey);
818             throw new InvalidSettingException(msg);
819         }
820         if (connStr.contains("%s")) {
821             final File directory = getDataDirectory();
822             String fileName = null;
823             if (dbFileNameKey != null) {
824                 fileName = Settings.getString(dbFileNameKey);
825             }
826             if (fileName == null) {
827                 final String msg = String.format("Invalid properties file to get a file based connection string; '%s' must be defined.",
828                         dbFileNameKey);
829                 throw new InvalidSettingException(msg);
830             }
831             if (connStr.startsWith("jdbc:h2:file:") && fileName.endsWith(".h2.db")) {
832                 fileName = fileName.substring(0, fileName.length() - 6);
833             }
834             // yes, for H2 this path won't actually exists - but this is sufficient to get the value needed
835             final File dbFile = new File(directory, fileName);
836             final String cString = String.format(connStr, dbFile.getCanonicalPath());
837             LOGGER.debug("Connection String: '{}'", cString);
838             return cString;
839         }
840         return connStr;
841     }
842 
843     /**
844      * Retrieves the directory that the JAR file exists in so that we can ensure we always use a common data directory for the
845      * embedded H2 database. This is public solely for some unit tests; otherwise this should be private.
846      *
847      * @return the data directory to store data files
848      * @throws IOException is thrown if an IOException occurs of course...
849      */
850     public static File getDataDirectory() throws IOException {
851         final File path = Settings.getDataFile(Settings.KEYS.DATA_DIRECTORY);
852         if (path.exists() || path.mkdirs()) {
853             return path;
854         }
855         throw new IOException(String.format("Unable to create the data directory '%s'", path.getAbsolutePath()));
856     }
857 }