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