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 }