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