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