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 for the telling us how many cve.url.* URLs exists. This is used in combination with CVE_BASE_URL to
122 * be able to retrieve the URLs for all of the files that make up the NVD CVE listing.
123 */
124 public static final String CVE_START_YEAR = "cve.startyear";
125 /**
126 * The properties key for the CVE schema version 1.2.
127 */
128 public static final String CVE_SCHEMA_1_2 = "cve.url-1.2.base";
129 /**
130 * The properties key for the CVE schema version 2.0.
131 */
132 public static final String CVE_SCHEMA_2_0 = "cve.url-2.0.base";
133 /**
134 * The properties key that indicates how often the CPE data needs to be updated.
135 */
136 public static final String CPE_MODIFIED_VALID_FOR_DAYS = "cpe.validfordays";
137 /**
138 * The properties key for the URL to retrieve the CPE.
139 */
140 public static final String CPE_URL = "cpe.url";
141 /**
142 * The properties key for the proxy server.
143 *
144 * @deprecated use {@link org.owasp.dependencycheck.utils.Settings.KEYS#PROXY_SERVER} instead.
145 */
146 @Deprecated
147 public static final String PROXY_URL = "proxy.server";
148 /**
149 * The properties key for the proxy server.
150 */
151 public static final String PROXY_SERVER = "proxy.server";
152 /**
153 * The properties key for the proxy port - this must be an integer value.
154 */
155 public static final String PROXY_PORT = "proxy.port";
156 /**
157 * The properties key for the proxy username.
158 */
159 public static final String PROXY_USERNAME = "proxy.username";
160 /**
161 * The properties key for the proxy password.
162 */
163 public static final String PROXY_PASSWORD = "proxy.password";
164 /**
165 * The properties key for the connection timeout.
166 */
167 public static final String CONNECTION_TIMEOUT = "connection.timeout";
168 /**
169 * The location of the temporary directory.
170 */
171 public static final String TEMP_DIRECTORY = "temp.directory";
172 /**
173 * The maximum number of threads to allocate when downloading files.
174 */
175 public static final String MAX_DOWNLOAD_THREAD_POOL_SIZE = "max.download.threads";
176 /**
177 * The key for a list of suppression files.
178 */
179 public static final String SUPPRESSION_FILE = "suppression.file";
180 /**
181 * The properties key for whether the Jar Analyzer is enabled.
182 */
183 public static final String ANALYZER_JAR_ENABLED = "analyzer.jar.enabled";
184 /**
185 * The properties key for whether the Archive analyzer is enabled.
186 */
187 public static final String ANALYZER_ARCHIVE_ENABLED = "analyzer.archive.enabled";
188 /**
189 * The properties key for whether the node.js package analyzer is enabled.
190 */
191 public static final String ANALYZER_NODE_PACKAGE_ENABLED = "analyzer.node.package.enabled";
192 /**
193 * The properties key for whether the composer lock file analyzer is enabled.
194 */
195 public static final String ANALYZER_COMPOSER_LOCK_ENABLED = "analyzer.composer.lock.enabled";
196 /**
197 * The properties key for whether the Python Distribution analyzer is enabled.
198 */
199 public static final String ANALYZER_PYTHON_DISTRIBUTION_ENABLED = "analyzer.python.distribution.enabled";
200 /**
201 * The properties key for whether the Python Package analyzer is enabled.
202 */
203 public static final String ANALYZER_PYTHON_PACKAGE_ENABLED = "analyzer.python.package.enabled";
204 /**
205 * The properties key for whether the Ruby Gemspec Analyzer is enabled.
206 */
207 public static final String ANALYZER_RUBY_GEMSPEC_ENABLED = "analyzer.ruby.gemspec.enabled";
208 /**
209 * The properties key for whether the Autoconf analyzer is enabled.
210 */
211 public static final String ANALYZER_AUTOCONF_ENABLED = "analyzer.autoconf.enabled";
212 /**
213 * The properties key for whether the CMake analyzer is enabled.
214 */
215 public static final String ANALYZER_CMAKE_ENABLED = "analyzer.cmake.enabled";
216 /**
217 * The properties key for whether the .NET Assembly analyzer is enabled.
218 */
219 public static final String ANALYZER_ASSEMBLY_ENABLED = "analyzer.assembly.enabled";
220 /**
221 * The properties key for whether the .NET Nuspec analyzer is enabled.
222 */
223 public static final String ANALYZER_NUSPEC_ENABLED = "analyzer.nuspec.enabled";
224 /**
225 * The properties key for whether the Nexus analyzer is enabled.
226 */
227 public static final String ANALYZER_NEXUS_ENABLED = "analyzer.nexus.enabled";
228 /**
229 * The properties key for the Nexus search URL.
230 */
231 public static final String ANALYZER_NEXUS_URL = "analyzer.nexus.url";
232 /**
233 * The properties key for using the proxy to reach Nexus.
234 */
235 public static final String ANALYZER_NEXUS_PROXY = "analyzer.nexus.proxy";
236 /**
237 * The properties key for whether the Central analyzer is enabled.
238 */
239 public static final String ANALYZER_CENTRAL_ENABLED = "analyzer.central.enabled";
240 /**
241 * The properties key for whether the OpenSSL analyzer is enabled.
242 */
243 public static final String ANALYZER_OPENSSL_ENABLED = "analyzer.openssl.enabled";
244 /**
245 * The properties key for the Central search URL.
246 */
247 public static final String ANALYZER_CENTRAL_URL = "analyzer.central.url";
248 /**
249 * The path to mono, if available.
250 */
251 public static final String ANALYZER_ASSEMBLY_MONO_PATH = "analyzer.assembly.mono.path";
252 /**
253 * The additional configured zip file extensions, if available.
254 */
255 public static final String ADDITIONAL_ZIP_EXTENSIONS = "extensions.zip";
256 /**
257 * The properties key for whether Test Scope dependencies should be skipped.
258 */
259 public static final String SKIP_TEST_SCOPE = "skip.test.scope";
260 /**
261 * The properties key for whether Runtime Scope dependencies should be skipped.
262 */
263 public static final String SKIP_RUNTIME_SCOPE = "skip.runtime.scope";
264 /**
265 * The properties key for whether Provided Scope dependencies should be skipped.
266 */
267 public static final String SKIP_PROVIDED_SCOPE = "skip.provided.scope";
268
269 /**
270 * The key to obtain the path to the VFEED data file.
271 */
272 public static final String VFEED_DATA_FILE = "vfeed.data_file";
273 /**
274 * The key to obtain the VFEED connection string.
275 */
276 public static final String VFEED_CONNECTION_STRING = "vfeed.connection_string";
277
278 /**
279 * The key to obtain the base download URL for the VFeed data file.
280 */
281 public static final String VFEED_DOWNLOAD_URL = "vfeed.download_url";
282 /**
283 * The key to obtain the download file name for the VFeed data.
284 */
285 public static final String VFEED_DOWNLOAD_FILE = "vfeed.download_file";
286 /**
287 * The key to obtain the VFeed update status.
288 */
289 public static final String VFEED_UPDATE_STATUS = "vfeed.update_status";
290
291 /**
292 * The HTTP request method for query last modified date.
293 */
294 public static final String DOWNLOADER_QUICK_QUERY_TIMESTAMP = "downloader.quick.query.timestamp";
295 }
296 //</editor-fold>
297
298 /**
299 * The logger.
300 */
301 private static final Logger LOGGER = LoggerFactory.getLogger(Settings.class);
302 /**
303 * The properties file location.
304 */
305 private static final String PROPERTIES_FILE = "dependencycheck.properties";
306 /**
307 * Thread local settings.
308 */
309 private static ThreadLocal<Settings> localSettings = new ThreadLocal<Settings>();
310 /**
311 * The properties.
312 */
313 private Properties props = null;
314
315 /**
316 * Private constructor for the Settings class. This class loads the properties files.
317 *
318 * @param propertiesFilePath the path to the base properties file to load
319 */
320 private Settings(String propertiesFilePath) {
321 InputStream in = null;
322 props = new Properties();
323 try {
324 in = this.getClass().getClassLoader().getResourceAsStream(propertiesFilePath);
325 props.load(in);
326 } catch (IOException ex) {
327 LOGGER.error("Unable to load default settings.");
328 LOGGER.debug("", ex);
329 } finally {
330 if (in != null) {
331 try {
332 in.close();
333 } catch (IOException ex) {
334 LOGGER.trace("", ex);
335 }
336 }
337 }
338 logProperties("Properties loaded", props);
339 }
340
341 /**
342 * Initializes the thread local settings object. Note, to use the settings object you must call this method. However, you must
343 * also call Settings.cleanup() to properly release resources.
344 */
345 public static void initialize() {
346 localSettings.set(new Settings(PROPERTIES_FILE));
347 }
348
349 /**
350 * Initializes the thread local settings object. Note, to use the settings object you must call this method. However, you must
351 * also call Settings.cleanup() to properly release resources.
352 *
353 * @param propertiesFilePath the path to the base properties file to load
354 */
355 public static void initialize(String propertiesFilePath) {
356 localSettings.set(new Settings(propertiesFilePath));
357 }
358
359 /**
360 * Cleans up resources to prevent memory leaks.
361 *
362 */
363 public static void cleanup() {
364 cleanup(true);
365 }
366
367 /**
368 * Cleans up resources to prevent memory leaks.
369 *
370 * @param deleteTemporary flag indicating whether any temporary directories generated should be removed
371 */
372 public static void cleanup(boolean deleteTemporary) {
373 if (deleteTemporary && tempDirectory != null && tempDirectory.exists()) {
374 FileUtils.delete(tempDirectory);
375 if (tempDirectory.exists()) {
376 try {
377 Thread.sleep(2000);
378 } catch (InterruptedException ex) {
379 LOGGER.trace("ignore", ex);
380 }
381 FileUtils.delete(tempDirectory);
382 }
383 }
384 try {
385 localSettings.remove();
386 } catch (Throwable ex) {
387 LOGGER.debug("Error cleaning up Settings", ex);
388 }
389 }
390
391 /**
392 * Gets the underlying instance of the Settings object.
393 *
394 * @return the Settings object
395 */
396 public static Settings getInstance() {
397 return localSettings.get();
398 }
399
400 /**
401 * Sets the instance of the Settings object to use in this thread.
402 *
403 * @param instance the instance of the settings object to use in this thread
404 */
405 public static void setInstance(Settings instance) {
406 localSettings.set(instance);
407 }
408
409 /**
410 * Logs the properties. This will not log any properties that contain 'password' in the key.
411 *
412 * @param header the header to print with the log message
413 * @param properties the properties to log
414 */
415 private static void logProperties(String header, Properties properties) {
416 if (LOGGER.isDebugEnabled()) {
417 final StringWriter sw = new StringWriter();
418 PrintWriter pw = null;
419 try {
420 pw = new PrintWriter(sw);
421 pw.format("%s:%n%n", header);
422 final Enumeration<?> e = properties.propertyNames();
423 while (e.hasMoreElements()) {
424 final String key = (String) e.nextElement();
425 if (key.contains("password")) {
426 pw.format("%s='*****'%n", key);
427 } else {
428 final String value = properties.getProperty(key);
429 if (value != null) {
430 pw.format("%s='%s'%n", key, value);
431 }
432 }
433 }
434 pw.flush();
435 LOGGER.debug(sw.toString());
436 } finally {
437 if (pw != null) {
438 pw.close();
439 }
440 }
441
442 }
443 }
444
445 /**
446 * Sets a property value.
447 *
448 * @param key the key for the property
449 * @param value the value for the property
450 */
451 public static void setString(String key, String value) {
452 localSettings.get().props.setProperty(key, value);
453 LOGGER.debug("Setting: {}='{}'", key, value);
454 }
455
456 /**
457 * Sets a property value.
458 *
459 * @param key the key for the property
460 * @param value the value for the property
461 */
462 public static void setBoolean(String key, boolean value) {
463 if (value) {
464 localSettings.get().props.setProperty(key, Boolean.TRUE.toString());
465 } else {
466 localSettings.get().props.setProperty(key, Boolean.FALSE.toString());
467 }
468 LOGGER.debug("Setting: {}='{}'", key, value);
469 }
470
471 /**
472 * Merges a new properties file into the current properties. This method allows for the loading of a user provided properties
473 * file.<br/><br/>
474 * Note: even if using this method - system properties will be loaded before properties loaded from files.
475 *
476 * @param filePath the path to the properties file to merge.
477 * @throws FileNotFoundException is thrown when the filePath points to a non-existent file
478 * @throws IOException is thrown when there is an exception loading/merging the properties
479 */
480 public static void mergeProperties(File filePath) throws FileNotFoundException, IOException {
481 FileInputStream fis = null;
482 try {
483 fis = new FileInputStream(filePath);
484 mergeProperties(fis);
485 } finally {
486 if (fis != null) {
487 try {
488 fis.close();
489 } catch (IOException ex) {
490 LOGGER.trace("close error", ex);
491 }
492 }
493 }
494 }
495
496 /**
497 * Merges a new properties file into the current properties. This method allows for the loading of a user provided properties
498 * file.<br/><br/>
499 * Note: even if using this method - system properties will be loaded before properties loaded from files.
500 *
501 * @param filePath the path to the properties file to merge.
502 * @throws FileNotFoundException is thrown when the filePath points to a non-existent file
503 * @throws IOException is thrown when there is an exception loading/merging the properties
504 */
505 public static void mergeProperties(String filePath) throws FileNotFoundException, IOException {
506 FileInputStream fis = null;
507 try {
508 fis = new FileInputStream(filePath);
509 mergeProperties(fis);
510 } finally {
511 if (fis != null) {
512 try {
513 fis.close();
514 } catch (IOException ex) {
515 LOGGER.trace("close error", ex);
516 }
517 }
518 }
519 }
520
521 /**
522 * Merges a new properties file into the current properties. This method allows for the loading of a user provided properties
523 * file.<br/><br/>
524 * Note: even if using this method - system properties will be loaded before properties loaded from files.
525 *
526 * @param stream an Input Stream pointing at a properties file to merge
527 * @throws IOException is thrown when there is an exception loading/merging the properties
528 */
529 public static void mergeProperties(InputStream stream) throws IOException {
530 localSettings.get().props.load(stream);
531 logProperties("Properties updated via merge", localSettings.get().props);
532 }
533
534 /**
535 * Returns a value from the properties file as a File object. If the value was specified as a system property or passed in via
536 * the -Dprop=value argument - this method will return the value from the system properties before the values in the contained
537 * configuration file.
538 *
539 * @param key the key to lookup within the properties file
540 * @return the property from the properties file converted to a File object
541 */
542 public static File getFile(String key) {
543 final String file = getString(key);
544 if (file == null) {
545 return null;
546 }
547 return new File(file);
548 }
549
550 /**
551 * Returns a value from the properties file as a File object. If the value was specified as a system property or passed in via
552 * the -Dprop=value argument - this method will return the value from the system properties before the values in the contained
553 * configuration file.
554 *
555 * This method will check the configured base directory and will use this as the base of the file path. Additionally, if the
556 * base directory begins with a leading "[JAR]\" sequence with the path to the folder containing the JAR file containing this
557 * class.
558 *
559 * @param key the key to lookup within the properties file
560 * @return the property from the properties file converted to a File object
561 */
562 protected static File getDataFile(String key) {
563 final String file = getString(key);
564 LOGGER.debug("Settings.getDataFile() - file: '{}'", file);
565 if (file == null) {
566 return null;
567 }
568 if (file.startsWith("[JAR]")) {
569 LOGGER.debug("Settings.getDataFile() - transforming filename");
570 final File jarPath = getJarPath();
571 LOGGER.debug("Settings.getDataFile() - jar file: '{}'", jarPath.toString());
572 final File retVal = new File(jarPath, file.substring(6));
573 LOGGER.debug("Settings.getDataFile() - returning: '{}'", retVal.toString());
574 return retVal;
575 }
576 return new File(file);
577 }
578
579 /**
580 * Attempts to retrieve the folder containing the Jar file containing the Settings class.
581 *
582 * @return a File object
583 */
584 private static File getJarPath() {
585 final String jarPath = Settings.class.getProtectionDomain().getCodeSource().getLocation().getPath();
586 String decodedPath = ".";
587 try {
588 decodedPath = URLDecoder.decode(jarPath, "UTF-8");
589 } catch (UnsupportedEncodingException ex) {
590 LOGGER.trace("", ex);
591 }
592
593 final File path = new File(decodedPath);
594 if (path.getName().toLowerCase().endsWith(".jar")) {
595 return path.getParentFile();
596 } else {
597 return new File(".");
598 }
599 }
600
601 /**
602 * Returns a value from the properties file. If the value was specified as a system property or passed in via the -Dprop=value
603 * argument - this method will return the value from the system properties before the values in the contained configuration
604 * file.
605 *
606 * @param key the key to lookup within the properties file
607 * @param defaultValue the default value for the requested property
608 * @return the property from the properties file
609 */
610 public static String getString(String key, String defaultValue) {
611 final String str = System.getProperty(key, localSettings.get().props.getProperty(key, defaultValue));
612 return str;
613 }
614
615 /**
616 * A reference to the temporary directory; used incase it needs to be deleted during cleanup.
617 */
618 private static File tempDirectory = null;
619
620 /**
621 * Returns the temporary directory.
622 *
623 * @return the temporary directory
624 * @throws java.io.IOException thrown if the temporary directory does not exist and cannot be created
625 */
626 public static File getTempDirectory() throws IOException {
627 final File tmpDir = new File(Settings.getString(Settings.KEYS.TEMP_DIRECTORY, System.getProperty("java.io.tmpdir")), "dctemp");
628 if (!tmpDir.exists() && !tmpDir.mkdirs()) {
629 final String msg = String.format("Unable to make a temporary folder '%s'", tmpDir.getPath());
630 throw new IOException(msg);
631 }
632 tempDirectory = tmpDir;
633 return tmpDir;
634 }
635
636 /**
637 * Returns a value from the properties file. If the value was specified as a system property or passed in via the -Dprop=value
638 * argument - this method will return the value from the system properties before the values in the contained configuration
639 * file.
640 *
641 * @param key the key to lookup within the properties file
642 * @return the property from the properties file
643 */
644 public static String getString(String key) {
645 return System.getProperty(key, localSettings.get().props.getProperty(key));
646 }
647
648 /**
649 * Removes a property from the local properties collection. This is mainly used in test cases.
650 *
651 * @param key the property key to remove
652 */
653 public static void removeProperty(String key) {
654 localSettings.get().props.remove(key);
655 }
656
657 /**
658 * Returns an int value from the properties file. If the value was specified as a system property or passed in via the
659 * -Dprop=value argument - this method will return the value from the system properties before the values in the contained
660 * configuration file.
661 *
662 * @param key the key to lookup within the properties file
663 * @return the property from the properties file
664 * @throws InvalidSettingException is thrown if there is an error retrieving the setting
665 */
666 public static int getInt(String key) throws InvalidSettingException {
667 int value;
668 try {
669 value = Integer.parseInt(Settings.getString(key));
670 } catch (NumberFormatException ex) {
671 throw new InvalidSettingException("Could not convert property '" + key + "' to an int.", ex);
672 }
673 return value;
674 }
675
676 /**
677 * Returns an int value from the properties file. If the value was specified as a system property or passed in via the
678 * -Dprop=value argument - this method will return the value from the system properties before the values in the contained
679 * configuration file.
680 *
681 * @param key the key to lookup within the properties file
682 * @param defaultValue the default value to return
683 * @return the property from the properties file or the defaultValue if the property does not exist or cannot be converted to
684 * an integer
685 */
686 public static int getInt(String key, int defaultValue) {
687 int value;
688 try {
689 value = Integer.parseInt(Settings.getString(key));
690 } catch (NumberFormatException ex) {
691 LOGGER.trace("Could not convert property '{}' to an int.", key, ex);
692 value = defaultValue;
693 }
694 return value;
695 }
696
697 /**
698 * Returns a long value from the properties file. If the value was specified as a system property or passed in via the
699 * -Dprop=value argument - this method will return the value from the system properties before the values in the contained
700 * configuration file.
701 *
702 * @param key the key to lookup within the properties file
703 * @return the property from the properties file
704 * @throws InvalidSettingException is thrown if there is an error retrieving the setting
705 */
706 public static long getLong(String key) throws InvalidSettingException {
707 long value;
708 try {
709 value = Long.parseLong(Settings.getString(key));
710 } catch (NumberFormatException ex) {
711 throw new InvalidSettingException("Could not convert property '" + key + "' to an int.", ex);
712 }
713 return value;
714 }
715
716 /**
717 * Returns a boolean value from the properties file. If the value was specified as a system property or passed in via the
718 * <code>-Dprop=value</code> argument this method will return the value from the system properties before the values in the
719 * contained configuration file.
720 *
721 * @param key the key to lookup within the properties file
722 * @return the property from the properties file
723 * @throws InvalidSettingException is thrown if there is an error retrieving the setting
724 */
725 public static boolean getBoolean(String key) throws InvalidSettingException {
726 boolean value;
727 try {
728 value = Boolean.parseBoolean(Settings.getString(key));
729 } catch (NumberFormatException ex) {
730 throw new InvalidSettingException("Could not convert property '" + key + "' to an int.", ex);
731 }
732 return value;
733 }
734
735 /**
736 * Returns a boolean value from the properties file. If the value was specified as a system property or passed in via the
737 * <code>-Dprop=value</code> argument this method will return the value from the system properties before the values in the
738 * contained configuration file.
739 *
740 * @param key the key to lookup within the properties file
741 * @param defaultValue the default value to return if the setting does not exist
742 * @return the property from the properties file
743 * @throws InvalidSettingException is thrown if there is an error retrieving the setting
744 */
745 public static boolean getBoolean(String key, boolean defaultValue) throws InvalidSettingException {
746 boolean value;
747 try {
748 final String strValue = Settings.getString(key);
749 if (strValue == null) {
750 return defaultValue;
751 }
752 value = Boolean.parseBoolean(strValue);
753 } catch (NumberFormatException ex) {
754 throw new InvalidSettingException("Could not convert property '" + key + "' to an int.", ex);
755 }
756 return value;
757 }
758
759 /**
760 * Returns a connection string from the configured properties. If the connection string contains a %s, this method will
761 * determine the 'data' directory and replace the %s with the path to the data directory. If the data directory does not
762 * exists it will be created.
763 *
764 * @param connectionStringKey the property file key for the connection string
765 * @param dbFileNameKey the settings key for the db filename
766 * @return the connection string
767 * @throws IOException thrown the data directory cannot be created
768 * @throws InvalidSettingException thrown if there is an invalid setting
769 */
770 public static String getConnectionString(String connectionStringKey, String dbFileNameKey)
771 throws IOException, InvalidSettingException {
772 final String connStr = Settings.getString(connectionStringKey);
773 if (connStr == null) {
774 final String msg = String.format("Invalid properties file; %s is missing.", connectionStringKey);
775 throw new InvalidSettingException(msg);
776 }
777 if (connStr.contains("%s")) {
778 final File directory = getDataDirectory();
779 String fileName = null;
780 if (dbFileNameKey != null) {
781 fileName = Settings.getString(dbFileNameKey);
782 }
783 if (fileName == null) {
784 final String msg = String.format("Invalid properties file to get a file based connection string; '%s' must be defined.",
785 dbFileNameKey);
786 throw new InvalidSettingException(msg);
787 }
788 if (connStr.startsWith("jdbc:h2:file:") && fileName.endsWith(".h2.db")) {
789 fileName = fileName.substring(0, fileName.length() - 6);
790 }
791 // yes, for H2 this path won't actually exists - but this is sufficient to get the value needed
792 final File dbFile = new File(directory, fileName);
793 final String cString = String.format(connStr, dbFile.getCanonicalPath());
794 LOGGER.debug("Connection String: '{}'", cString);
795 return cString;
796 }
797 return connStr;
798 }
799
800 /**
801 * Retrieves the directory that the JAR file exists in so that we can ensure we always use a common data directory for the
802 * embedded H2 database. This is public solely for some unit tests; otherwise this should be private.
803 *
804 * @return the data directory to store data files
805 * @throws IOException is thrown if an IOException occurs of course...
806 */
807 public static File getDataDirectory() throws IOException {
808 final File path = Settings.getDataFile(Settings.KEYS.DATA_DIRECTORY);
809 if (path.exists() || path.mkdirs()) {
810 return path;
811 }
812 throw new IOException(String.format("Unable to create the data directory '%s'", path.getAbsolutePath()));
813 }
814 }