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