• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CeON / dataverse / 1363

13 May 2024 08:49AM UTC coverage: 25.179% (-0.004%) from 25.183%
1363

push

jenkins

web-flow
Closes #2468: Clean-up empty working directories during clear step (#2471)

1 of 15 new or added lines in 2 files covered. (6.67%)

165 existing lines in 5 files now uncovered.

17523 of 69594 relevant lines covered (25.18%)

0.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.36
/dataverse-webapp/src/main/java/edu/harvard/iq/dataverse/settings/SettingsServiceBean.java
1
package edu.harvard.iq.dataverse.settings;
2

3
import com.google.common.cache.CacheBuilder;
4
import com.google.common.cache.CacheLoader;
5
import com.google.common.cache.LoadingCache;
6
import edu.harvard.iq.dataverse.actionlogging.ActionLogServiceBean;
7
import edu.harvard.iq.dataverse.persistence.ActionLogRecord;
8
import edu.harvard.iq.dataverse.persistence.Setting;
9
import edu.harvard.iq.dataverse.persistence.SettingDao;
10
import edu.harvard.iq.dataverse.util.StringUtil;
11
import org.apache.commons.lang3.StringUtils;
12
import org.json.JSONArray;
13
import org.json.JSONException;
14
import org.json.JSONObject;
15
import org.slf4j.Logger;
16
import org.slf4j.LoggerFactory;
17

18
import javax.ejb.EJB;
19
import javax.enterprise.context.ApplicationScoped;
20
import java.util.ArrayList;
21
import java.util.Arrays;
22
import java.util.HashMap;
23
import java.util.List;
24
import java.util.Map;
25
import java.util.Set;
26

27
import static java.util.function.Function.identity;
28
import static java.util.stream.Collectors.toMap;
29

30
/**
31
 * Service bean accessing and manipulating application settings.
32
 * Settings are resolved from database and property files (db ones takes precedence).
33
 *
34
 * @author michael
35
 * @see FileBasedSettingsFetcher
36
 */
37
@ApplicationScoped
38
public class SettingsServiceBean {
1✔
39

40
    private static final String KEY_AND_POSTFIX_SEPARATOR = ".";
41

42
    /**
43
     * Some convenient keys for the settings. Note that the setting's
44
     * name is really a {@code String}, but it's good to have the compiler look
45
     * over your shoulder when typing strings in various places of a large app.
46
     * So there.
47
     */
48
    public enum Key {
1✔
49
        AllowApiTokenLookupViaApi,
1✔
50
        /**
51
         * Ordered, comma-separated list of custom fields to show above the fold
52
         * on dataset page such as "data_type,sample,pdb"
53
         */
54
        CustomDatasetSummaryFields,
1✔
55
        /**
56
         * Defines a public installation -- all datafiles are unrestricted
57
         */
58
        PublicInstall,
1✔
59
        /**
60
         * Enables the provenance collection popup.
61
         * Allows users to store their provenance json and description
62
         */
63
        ProvCollectionEnabled,
1✔
64
        /**
65
         * For example, https://datacapture.example.org
66
         */
67
        DataCaptureModuleUrl,
1✔
68
        RepositoryStorageAbstractionLayerUrl,
1✔
69
        UploadMethods,
1✔
70
        DownloadMethods,
1✔
71
        /**
72
         * If the data replicated around the world using RSAL (Repository
73
         * Storage Abstraction Layer) is locally available, this is its file
74
         * path, such as "/programs/datagrid".
75
         * <p>
76
         * TODO: Think about if it makes sense to make this a column in the
77
         * StorageSite database table.
78
         */
79
        LocalDataAccessPath,
1✔
80
        IdentifierGenerationStyle,
1✔
81
        OAuth2CallbackUrl,
1✔
82
        DefaultAuthProvider,
1✔
83
        FooterCopyright,
1✔
84
        FileFixityChecksumAlgorithm,
1✔
85
        MinutesUntilConfirmEmailTokenExpires,
1✔
86
        /**
87
         * Override Solr highlighting "fragsize"
88
         * https://wiki.apache.org/solr/HighlightingParameters#hl.fragsize
89
         */
90
        SearchHighlightFragmentSize,
1✔
91
        /**
92
         * Experimental: Allow non-public search with a key/token using the
93
         * Search API. See also https://github.com/IQSS/dataverse/issues/1299
94
         */
95
        SearchApiNonPublicAllowed,
1✔
96
        /**
97
         * In Dataverse 4.7 and earlier, an API token was required to use the
98
         * Search API. Tokens are no longer required but you can revert to the
99
         * old behavior by setting this to false.
100
         */
101
        SearchApiRequiresToken,
1✔
102

103
        /**
104
         * API endpoints that are not accessible. Comma separated list.
105
         */
106
        BlockedApiEndpoints,
1✔
107

108
        /**
109
         * A key that, with the right {@link ApiBlockingFilter.BlockPolicy},
110
         * allows calling blocked APIs.
111
         */
112
        BlockedApiKey,
1✔
113

114

115
        /**
116
         * How to treat blocked APIs. One of drop, localhost-only, unblock-key
117
         */
118
        BlockedApiPolicy,
1✔
119

120
        /**
121
         * For development only (see dev guide for details). Backed by an enum
122
         * of possible account types.
123
         */
124
        DebugShibAccountType,
1✔
125
        DebugOAuthAccountType,
1✔
126
        /**
127
         * Application-wide Terms of Use per installation.
128
         * Setting can be postfixed with language code to
129
         * obtain translated versions of terms of use.
130
         * It is assumed that not postfixed setting is
131
         * the default one (used in case if language specific
132
         * version is not present).
133
         */
134
        ApplicationTermsOfUse,
1✔
135
        /**
136
         * Terms of Use specific to API per installation.
137
         */
138
        ApiTermsOfUse,
1✔
139
        /**
140
         * URL for the application-wide Privacy Policy per installation, linked
141
         * to from the footer.
142
         */
143
        ApplicationPrivacyPolicyUrl,
1✔
144
        /**
145
         * Solr hostname and port, such as "localhost:8983".
146
         */
147
        SolrHostColonPort,
1✔
148
        /**
149
         * Enable full-text indexing in solr
150
         * Defaults to false
151
         **/
152
        SolrFullTextIndexing,
1✔
153
        /**
154
         * If file size of indexed file is greater than this value
155
         * then full text indexing will not take place
156
         * If set to 0 then no limit
157
         * Defaults to 0
158
         */
159
        SolrMaxFileSizeForFullTextIndexing, //
1✔
160
        /**
161
         * Key for limiting the number of bytes uploaded via the Data Deposit API, UI
162
         * If not set then not limit
163
         **/
164
        MaxFileUploadSizeInBytes,
1✔
165
        /**
166
         * Key for if ScrubMigrationData is enabled or disabled.
167
         */
168
        ScrubMigrationData,
1✔
169
        /**
170
         * Key for the url to send users who want to sign up to.
171
         */
172
        SignUpUrl,
1✔
173
        /**
174
         * Key for whether we allow users to sign up
175
         */
176
        AllowSignUp,
1✔
177
        /**
178
         * protocol for global id
179
         */
180
        Protocol,
1✔
181
        /**
182
         * authority for global id
183
         */
184
        Authority,
1✔
185
        /**
186
         * DoiProvider for global id
187
         */
188
        DoiProvider,
1✔
189
        /**
190
         * Period in ms after which the service will try to reserve yet unreserved DOI's
191
         */
192
        DoiBackgroundReservationInterval,
1✔
193
        /**
194
         * Shoulder for global id - used to create a common prefix on identifiers
195
         */
196
        Shoulder,
1✔
197

198
        /**
199
         * Optionally override http://guides.dataverse.org .
200
         */
201
        GuidesBaseUrl,
1✔
202

203
        /**
204
         * A link to an installation of https://github.com/IQSS/miniverse or
205
         * some other metrics app.
206
         */
207
        MetricsUrl,
1✔
208

209
        /**
210
         * Number of minutes before a metrics query can be rerun. Otherwise a cached value is returned.
211
         * Previous month dates always return cache. Only applies to new internal caching system (not miniverse).
212
         */
213
        MetricsCacheTimeoutMinutes,
1✔
214
        /* zip download size limit */
215
        /**
216
         * Optionally override version number in guides.
217
         */
218
        GuidesVersion,
1✔
219
        /**
220
         * Download-as-zip size limit.
221
         * If set to 0 then no limit.
222
         * If set to -1 then zip downloads are disabled
223
         */
224
        ZipDownloadLimit,
1✔
225
        /**
226
         * Number of datafiles that we allow to be created through
227
         * zip file upload.
228
         */
229
        ZipUploadFilesLimit,
1✔
230
        /**
231
         * the number of files the GUI user is allowed to upload in one batch,
232
         * via drag-and-drop, or through the file select dialog
233
         */
234
        MultipleUploadFilesLimit,
1✔
235

236
        /**
237
         * status message that will appear on the home page
238
         */
239
        StatusMessageHeader,
1✔
240
        /**
241
         * full text of status message, to appear in popup
242
         */
243
        StatusMessageText,
1✔
244
        /**
245
         * return email address for system emails such as notifications
246
         */
247
        SystemEmail,
1✔
248
        /**
249
         * size limit for Tabular data file ingests <br/>
250
         * (can be set separately for specific ingestable formats; in which
251
         * case the actual stored option will be TabularIngestSizeLimit:{FORMAT_NAME}
252
         * where {FORMAT_NAME} is the format identification tag returned by the
253
         * getFormatName() method in the format-specific plugin; "sav" for the
254
         * SPSS/sav format, "RData" for R, etc.
255
         * for example: :TabularIngestSizeLimit:RData <br/>
256
         * -1 means no limit is set;
257
         * 0 on the other hand would mean that ingest is fully disabled for tabular data.
258
         */
259
        TabularIngestSizeLimit,
1✔
260
        /**
261
         * Files with the number of cells (ie. cases times variables) above
262
         * that number will be ingested using different method of reading
263
         * and storing intermediate data (with the same final result), which
264
         * may be more time-consuming but less resource-consuming.
265
         * Those with number of cells below or equal to that number will
266
         * be processed in-memory.
267
         */
268
        IngestMethodChangeThreshold,
1✔
269
        /**
270
         * If the number of variables of ingested file exceeds that limit,
271
         * the ingest will fail.
272
         */
273
        IngestedVariablesLimit,
1✔
274
        /**
275
         * Whether to allow user to create GeoConnect Maps
276
         * This boolean effects whether the user sees the map button on
277
         * the dataset page and if the ingest will create a shape file
278
         * Default is false
279
         */
280
        GeoconnectCreateEditMaps,
1✔
281
        /**
282
         * Whether to allow a user to view existing maps
283
         * This boolean effects whether a user may see the
284
         * Explore World Map Button
285
         * Default is false;
286
         */
287
        GeoconnectViewMaps,
1✔
288
        /**
289
         * The message added to a popup upon dataset publish
290
         */
291
        DatasetPublishPopupCustomText,
1✔
292
        /**
293
         * Whether to display the publish text for every published version
294
         */
295
        DatasetPublishPopupCustomTextOnAllVersions,
1✔
296
        /**
297
         * Whether Harvesting (OAI) service is enabled
298
         */
299
        OAIServerEnabled,
1✔
300

301
        /**
302
         * Whether Shibboleth passive authentication mode is enabled
303
         */
304
        ShibPassiveLoginEnabled,
1✔
305
        /**
306
         * Convert Shibboleth AJP attributes from ISO-8859-1 to UTF-8
307
         */
308
        ShibAttributeCharacterSetConversionEnabled,
1✔
309
        /**
310
         * Whether Export should exclude FieldType.EMAIL
311
         */
312
        ExcludeEmailFromExport,
1✔
313
        /**
314
         * Location and name of HomePage customization file
315
         */
316
        HomePageCustomizationFile,
1✔
317
        /**
318
         * Location and name of Header customization file
319
         */
320
        HeaderCustomizationFile,
1✔
321
        /**
322
         * Location and name of Footer customization file
323
         */
324
        FooterCustomizationFile,
1✔
325
        /**
326
         * Location and name of CSS customization file (it will be used as an inline style)
327
         */
328
        StyleCustomizationFile,
1✔
329
        /**
330
         * Location and name of analytics code file
331
         */
332
        WebAnalyticsCode,
1✔
333
        /**
334
         * Location and name of installation logo customization file
335
         */
336
        LogoCustomizationFile,
1✔
337

338
        // Option to override the navbar url underlying the "About" link
339
        NavbarAboutUrl,
1✔
340

341
        // Option to override multiple guides with a single url
342
        NavbarGuidesUrl,
1✔
343

344
        // Option to overide the feedback dialog display with a link to an external page via its url
345
        NavbarSupportUrl,
1✔
346

347
        /**
348
         * The theme for the root dataverse can get in the way when you try make
349
         * use of HeaderCustomizationFile and LogoCustomizationFile so this is a
350
         * way to disable it.
351
         */
352
        DisableRootDataverseTheme,
1✔
353
        // Limit on how many guestbook entries to display on the guestbook-responses page:
354
        GuestbookResponsesPageDisplayLimit,
1✔
355

356
        /**
357
         * The dictionary filepaths separated by a pipe (|)
358
         */
359
        PVDictionaries,
1✔
360

361
        /**
362
         * The minimum length of a good, long, strong password.
363
         * Defaults to 20.
364
         */
365
        PVGoodStrength,
1✔
366

367
        /**
368
         * A password minimum length
369
         * Defaults to 6
370
         */
371
        PVMinLength,
1✔
372
        /**
373
         * A password maximum length
374
         * If set to 0 then maximum length is disabled
375
         */
376
        PVMaxLength,
1✔
377

378
        /**
379
         * One letter, 2 special characters, etc. (string in form Alphabetical:1,Digit:1)
380
         * Defaults to (string in form Alphabetical:1,Digit:1):
381
         * - one alphabetical
382
         * - one digit
383
         */
384
        PVCharacterRules,
1✔
385

386
        /**
387
         * The number of M characteristics.
388
         * Defaults to 2.
389
         */
390
        PVNumberOfCharacteristics,
1✔
391

392
        /**
393
         * The number of consecutive digits allowed for a password.
394
         * Defaults to highest int
395
         */
396
        PVNumberOfConsecutiveDigitsAllowed,
1✔
397
        /**
398
         * Configurable text for alert/info message on passwordreset.xhtml when users are required to update their password.
399
         */
400
        PVCustomPasswordResetAlertMessage,
1✔
401
        /**
402
         * String to describe DOI format for data files. Default is DEPENDENT.
403
         * 'DEPENEDENT' means the DOI will be the Dataset DOI plus a file DOI with a slash in between.
404
         * 'INDEPENDENT' means a new global id, completely independent from the dataset-level global id.
405
         */
406
        DataFilePIDFormat,
1✔
407
        /**
408
         * Json array of supported languages
409
         */
410
        Languages,
1✔
411
        /**
412
         * Number for the minimum number of files to send PID registration to asynchronous workflow
413
         */
414
        PIDAsynchRegFileCount,
1✔
415
        /**
416
         *
417
         */
418
        FilePIDsEnabled,
1✔
419

420
        /**
421
         * Indicates if the Handle service is setup to work 'independently' (No communication with the Global Handle Registry)
422
         */
423
        IndependentHandleService,
1✔
424

425
        /**
426
         * Archiving can be configured by providing an Archiver class name (class must extend AstractSubmitToArchiverCommand)
427
         * and a list of settings that should be passed to the Archiver.
428
         * Note:
429
         * Configuration may also require adding Archiver-specific jvm-options (i.e. for username and password) in glassfish.
430
         * <p>
431
         * To automate the submission of an archival copy step as part of publication, a post-publication workflow must also be configured.
432
         * <p>
433
         * For example:
434
         * ArchiverClassName - "edu.harvard.iq.dataverse.engine.command.impl.DPNSubmitToArchiveCommand"
435
         * ArchiverSettings - "DuraCloudHost, DuraCloudPort, DuraCloudContext"
436
         * <p>
437
         * Note: Dataverse must be configured with values for these dynamically defined settings as well, e.g.
438
         * <p>
439
         * DuraCloudHost , eg. "qdr.duracloud.org", a non-null value enables submission
440
         * DuraCloudPort, default is 443
441
         * DuraCloudContext, default is "durastore"
442
         */
443

444
        ArchiverClassName,
1✔
445
        ArchiverSettings,
1✔
446
        /**
447
         * A comma-separated list of roles for which new dataverses should inherit the
448
         * corresponding role assignments from the parent dataverse. Also affects
449
         * /api/admin/dataverse/{alias}/addRolesToChildren. Default is "", no
450
         * inheritance. "*" means inherit assignments for all roles
451
         */
452
        InheritParentRoleAssignments,
1✔
453

454
        /**
455
         * Indicates if other terms of use are active or not.
456
         */
457
        AllRightsReservedTermsOfUseActive,
1✔
458
        RestrictedAccessTermsOfUseActive,
1✔
459

460
        /**
461
         * Size limits for generating thumbnails on the fly
462
         * (i.e., we'll attempt to generate a thumbnail on the fly if the
463
         * size of the file is less than this)
464
         */
465
        ThumbnailImageSizeLimit,
1✔
466
        ThumbnailPDFSizeLimit,
1✔
467

468
        DropboxKey,
1✔
469
        DoiDataCiteCitationsPageUrl,
1✔
470
        DoiDataCiteRestApiUrl,
1✔
471
        DoiBaseUrlString,
1✔
472
        DoiUsername,
1✔
473
        DoiPassword,
1✔
474

475
        HandleNetAdmCredFile,
1✔
476
        HandleNetAdmPrivPhrase,
1✔
477
        HandleNetIndex,
1✔
478

479
        TimerServer,
1✔
480

481
        MinutesUntilPasswordResetTokenExpires,
1✔
482

483
        /**
484
         * Indicates if rserve is properly configured in this dataverse installation.
485
         * If not, then any functionality using rserve should be switched off
486
         */
487
        RserveConfigured,
1✔
488
        RserveHost,
1✔
489
        RservePort,
1✔
490
        RserveUser,
1✔
491
        RservePassword,
1✔
492
        RserveTempDir,
1✔
493

494
        /**
495
         * Some installations may not want download URLs to their files to be
496
         * available in Schema.org JSON-LD output.
497
         */
498
        HideSchemaDotOrgDownloadUrls,
1✔
499

500
        /**
501
         * A JVM option for specifying the "official" URL of the site.
502
         * Unlike the FQDN option above, this would be a complete URL,
503
         * with the protocol, port number etc.
504
         */
505
        SiteUrl,
1✔
506

507
        /**
508
         * Text with privacy policy content.
509
         * Setting can be postfixed with language code to
510
         * obtain translated versions of terms of use.
511
         * It is assumed that not postfixed setting is
512
         * the default one (used in case if language specific
513
         * version is not present).
514
         */
515
        PrivacyPolicy,
1✔
516

517
        /**
518
         * Text with accessibility statement.
519
         * Setting can be postfixed with language code to
520
         * obtain translated versions of the statement.
521
         * It is assumed that not postfixed setting is
522
         * the default one (used in case if language specific
523
         * version is not present).
524
         */
525
        AccessibilityStatement,
1✔
526

527
        /**
528
         * Dataverse admin can configure application-wide maximum length for embargo.
529
         * @value number of months
530
         * For any given date, maximum embargo for that date is: [date] + [MaximumEmbargoLength]
531
         * Example: for setting value = 3 and date 12DEC2019, effective maximum embargo date for user
532
         * trying to set embargo for his dataset is 12MAR2020 (3 months from [date])
533
         */
534
        MaximumEmbargoLength,
1✔
535

536
        /**
537
         * Application wide format for dates.
538
         * Use this whenever you want to print date on GUI.
539
         */
540
        DefaultDateFormat,
1✔
541

542
        /**
543
         * Email of mail overseer: if present, a copy of every mail sent by the application
544
         * will be sent to that address.
545
         *
546
         * By default it is not set, so the feature is turned off.
547
         */
548
        MailOverseerAddress,
1✔
549

550
        /**
551
         * Show link to Privacy Policy page in the footer (if set to 'true').
552
         * By default is set to 'false', so link won't be shown.
553
         */
554
        ShowPrivacyPolicyFooterLink,
1✔
555

556
        /**
557
         * Show link to Terms of Use page in the footer (if set to 'true').
558
         * By default is set to 'false', so link won't be shown.
559
         */
560
        ShowTermsOfUseFooterLink,
1✔
561

562
        /**
563
         * Show link to Accesibility Statement page in the footer (if set to 'true').
564
         * By default is set to 'false', so link won't be shown.
565
         */
566
        ShowAccessibilityStatementFooterLink,
1✔
567

568
        /**
569
         * Name of the site that will be presented in the header.
570
         * Setting can be postfixed with language code to
571
         * obtain translated versions.
572
         */
573
        SiteName,
1✔
574

575
        /**
576
         * Full name of the site that will be presented in the header
577
         * below {@link Key#SiteName}.
578
         * Setting can be postfixed with language code to
579
         * obtain translated versions.
580
         */
581
        SiteFullName,
1✔
582

583
        /**
584
         * Target link for the "superior logo".
585
         * Setting can be postfixed with language code to
586
         * obtain path a language-specific link.
587
         */
588
        SuperiorLogoLink,
1✔
589

590
        /**
591
         * Path to a "superior logo" to be presented in the header.
592
         * Setting can be postfixed with language code to
593
         * obtain path to a translated logo.
594
         */
595
        SuperiorLogoPath,
1✔
596

597
        /**
598
         * Path to a compact "superior logo" to be presented in the header.
599
         * Setting can be postfixed with language code to
600
         * obtain the path to a translated logo.
601
         */
602
        SuperiorLogoResponsivePath,
1✔
603

604
        /**
605
         * Path to a "superior logo" (high contrast version) to be presented in the header.
606
         * Setting can be postfixed with language code to
607
         * obtain path to a translated logo.
608
         */
609
        SuperiorLogoContrastPath,
1✔
610

611
        /**
612
         * Path to a compact "superior logo" (high contrast version) to be presented in the header.
613
         * Setting can be postfixed with language code to
614
         * obtain the path to a translated logo.
615
         */
616
        SuperiorLogoContrastResponsivePath,
1✔
617

618
        /**
619
         * Description (alt text) for the "superior logo".
620
         * Setting can be postfixed with language code to
621
         * obtain translated versions.
622
         */
623
        SuperiorLogoAlt,
1✔
624

625
        /**
626
         * Indicates if antivirus scanner is enabled
627
         */
628
        AntivirusScannerEnabled,
1✔
629
        /**
630
         * Maximum size of files that will be scanned
631
         * by antivirus
632
         */
633
        AntivirusScannerMaxFileSize,
1✔
634
        AntivirusScannerMaxFileSizeForExecutables,
1✔
635
        /**
636
         * Antivirus scanner engine settings
637
         */
638
        AntivirusScannerSocketAddress,
1✔
639
        AntivirusScannerSocketPort,
1✔
640
        AntivirusScannerSocketTimeout,
1✔
641

642
        FilesIntegrityCheckTimerExpression,
1✔
643

644
        /**
645
         * If set to true, the set of actions that could be performed by the
646
         * users with unconfirmed emails will be severely limited.
647
         */
648
        UnconfirmedMailRestrictionModeEnabled,
1✔
649

650
        /**
651
         * Command or path for external utility for RAR5 size checking
652
         */
653
        RarDataUtilCommand,
1✔
654
        /**
655
         * Additional options for rar utility
656
         */
657
        RarDataUtilOpts,
1✔
658
        /**
659
         * Character starting the line before the result (total size). The
660
         * line must be the last in the output that starts with the chosen
661
         * character. The next line has to start with result (with optional
662
         * spaces before).
663
         */
664
        RarDataLineBeforeResultDelimiter,
1✔
665

666
        /**
667
         * Sets the maximal size of gzip file (in bytes) that would be
668
         * subject to checking of uncompressed content size.
669
         */
670
        GzipMaxInputFileSizeInBytes,
1✔
671

672
        /**
673
         * Sets the maximal size of output file (in bytes) that could be
674
         * unpacked from gzip file in order to check its uncompressed size.
675
         */
676
        GzipMaxOutputFileSizeInBytes,
1✔
677

678
        /**
679
         * Cron expression that indicates how often
680
         * updating of citation counts should take place.
681
         * Note that current implementation is heavily based
682
         * on DataCite api. If your installation uses some
683
         * other global id provider then this process will
684
         * not work as expected.
685
         * If empty then updating of citation counts will be skipped.
686
         * Default value: empty.
687
         */
688
        CitationCountUpdateTimerExpression,
1✔
689

690
        /**
691
         * If true then instance will run in readonly mode.
692
         * Installation in readonly mode will have functionality
693
         * limited to operations that will not modify database and
694
         * storage state.
695
         */
696
        ReadonlyMode,
1✔
697

698
        /**
699
         * Maximal size (in bytes) of files that can be uploaded in a single
700
         * batch. If set to 0 then there's no limit.
701
         */
702
        SingleUploadBatchMaxSize,
1✔
703

704
        /**
705
         * Cron expression that indicates how often
706
         * checking of datasets after embargo for reindex should take place.
707
         * If empty then check will be skipped.
708
         * Default value: empty.
709
         */
710

711
        ReindexAfterEmbargoTimerExpression,
1✔
712

713
        /**
714
         * Wrap http requests with url from SiteUrl property
715
         * It is needed when glassfish is behind reverse proxy
716
         * for matching url in saml assertions with the server
717
         * url
718
         */
719
        SamlWrapHttpRequestUrl,
1✔
720

721
        /**
722
         * Additional (localized) text to show at the top
723
         * of the login page.
724
         */
725
        LoginInfo,
1✔
726

727
        /**
728
         * Additional (localized) text to show at the top
729
         * of the "Add dataset" modal window.
730
         */
731
        SelectDataverseInfo
1✔
732
        ;
733

734

735
        @Override
736
        public String toString() {
737
            return ":" + name();
1✔
738
        }
739
    }
740

741
    private static final Logger log = LoggerFactory.getLogger(SettingsServiceBean.class);
1✔
742

743
    @EJB
744
    private SettingDao settingDao;
745

746
    @EJB
747
    private ActionLogServiceBean actionLogSvc;
748

749
    @EJB
750
    private FileBasedSettingsFetcher fileBasedSettingsFetcher;
751

752
    private final CacheLoader<String, String> settingCacheLoader = new CacheLoader<String, String>() {
1✔
753
        @Override
754
        public String load(String key) {
UNCOV
755
            Setting s = settingDao.find(key);
×
UNCOV
756
            return (s != null) ? s.getContent() : fileBasedSettingsFetcher.getSetting(key);
×
757
        }
758
    };
759

760
    private final LoadingCache<String, String> settingCache = CacheBuilder.newBuilder()
1✔
761
            .build(settingCacheLoader);
1✔
762

763
    // -------------------- LOGIC --------------------
764

765
    /**
766
     * Basic functionality - get the name, return the setting from db if present or from properties file if not.
767
     *
768
     * @param name of the setting
769
     * @return the actual setting or empty string.
770
     */
771
    public String get(String name) {
UNCOV
772
        return settingCache.getUnchecked(name);
×
773
    }
774

775
    /**
776
     * Same as {@link #get(java.lang.String)}, but with static checking.
777
     *
778
     * @param key Enum value of the name.
779
     * @return The setting, or  empty string.
780
     */
781
    public String getValueForKey(Key key) {
782
        return get(key.toString());
1✔
783
    }
784

785
    /**
786
     * Returns value of setting with key that is postfixed.
787
     * It can be used if setting has static base key part and dynamic
788
     * postfix.
789
     * <p>
790
     * Example of such setting is {@link Key#ApplicationTermsOfUse}.
791
     * It can be posfixed with language code to obtain translated
792
     * values:
793
     * <code>
794
     * getValueForKeyWithPostfix(Key.ApplicationTermsOfUse, "de")
795
     * getValueForKeyWithPostfix(Key.ApplicationTermsOfUse, "fr")
796
     * </code>
797
     *
798
     * @param key
799
     * @param postfix
800
     * @return setting value or empty string if setting not present
801
     */
802
    public String getValueForKeyWithPostfix(Key key, String postfix) {
UNCOV
803
        return get(key.toString() + KEY_AND_POSTFIX_SEPARATOR + postfix);
×
804
    }
805

806
    /**
807
     * Attempt to convert the value to an integer
808
     * - Applicable for keys such as MaxFileUploadSizeInBytes
809
     * <p>
810
     * On failure (key not found or string not convertible to a long), returns null
811
     *
812
     * @param key
813
     * @return
814
     */
815
    public Long getValueForKeyAsLong(Key key) {
816

817
        String val = this.getValueForKey(key);
1✔
818

819
        if (StringUtils.isEmpty(val)) {
1✔
820
            return null;
1✔
821
        }
822

823
        try {
UNCOV
824
            return Long.parseLong(val);
×
UNCOV
825
        } catch (NumberFormatException ex) {
×
UNCOV
826
            log.warn("Incorrect setting. Could not convert \"{}\" from setting {} to long.", val, key.toString());
×
827
            return null;
×
828
        }
829

830
    }
831

832
    public Integer getValueForKeyAsInt(Key key) {
UNCOV
833
        Long value = getValueForKeyAsLong(key);
×
UNCOV
834
        if (value == null) {
×
835
            return null;
×
836
        }
UNCOV
837
        return value.intValue();
×
838
    }
839

840
    public List<String> getValueForKeyAsList(Key key) {
841
        return Arrays.asList(StringUtils.split(getValueForKey(key), ","));
×
842
    }
843

844
    public List<Map<String, String>> getValueForKeyAsListOfMaps(Key key) {
845
        List<Map<String, String>> list = new ArrayList<>();
×
846
        try {
847
            JSONArray entries = new JSONArray(getValueForKey(key));
×
848
            for (Object obj : entries) {
×
849
                JSONObject entry = (JSONObject) obj;
×
850
                list.add(entry.keySet().stream()
×
UNCOV
851
                        .collect(toMap(identity(), entry::getString)));
×
UNCOV
852
            }
×
UNCOV
853
        } catch (JSONException e) {
×
854
            log.warn("Error parsing setting " + key + " as JSON", e);
×
855
        }
×
856
        return list;
×
857
    }
858

859
    public Setting set(String name, String content) {
UNCOV
860
        settingCache.invalidate(name);
×
UNCOV
861
        Setting s = settingDao.save(new Setting(name, content));
×
862
        actionLogSvc.log(new ActionLogRecord(ActionLogRecord.ActionType.Setting, "set")
×
UNCOV
863
                                 .setInfo(name + ": " + content));
×
UNCOV
864
        return s;
×
865
    }
866

867
    public Setting setValueForKey(Key key, String content) {
UNCOV
868
        return set(key.toString(), content);
×
869
    }
870

871
    /**
872
     * The correct way to decide whether a string value in the
873
     * settings table should be considered as {@code true}.
874
     *
875
     * @param name name of the setting.
876
     * @return boolean value of the setting.
877
     */
878
    public boolean isTrue(String name) {
UNCOV
879
        String val = get(name);
×
UNCOV
880
        return StringUtil.isTrue(val);
×
881
    }
882

883
    public boolean isTrueForKey(Key key) {
UNCOV
884
        return isTrue(key.toString());
×
885
    }
886

887
    public void deleteValueForKey(Key name) {
888
        delete(name.toString());
×
889
    }
×
890

891
    public void delete(String name) {
UNCOV
892
        settingCache.invalidate(name);
×
893
        actionLogSvc.log(new ActionLogRecord(ActionLogRecord.ActionType.Setting, "delete")
×
UNCOV
894
                                 .setInfo(name));
×
895
        settingDao.delete(name);
×
896
    }
×
897

898
    public Map<String, String> listAll() {
899
        Map<String, String> mergedSettings = new HashMap<>();
×
900

901
        Map<String, String> fileSettings = fileBasedSettingsFetcher.getAllSettings();
×
UNCOV
902
        mergedSettings.putAll(fileSettings);
×
903

UNCOV
904
        List<Setting> dbSettings = settingDao.findAll();
×
905
        dbSettings.forEach(s -> mergedSettings.put(s.getName(), s.getContent()));
×
906

907
        return mergedSettings;
×
908
    }
909

910
    public Map<String, String> getFileBasedSettingsForPrefix(String prefix) {
UNCOV
911
        Map<String, String> settings = fileBasedSettingsFetcher.getAllSettings();
×
UNCOV
912
        Set<String> keys = settings.keySet();
×
UNCOV
913
        return keys.stream()
×
UNCOV
914
                .filter(k -> k.startsWith(prefix))
×
UNCOV
915
                .collect(HashMap::new, (m, k) -> m.put(k, settings.get(k)), Map::putAll);
×
916
    }
917
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc