/* * Edited and managed by the WP1 for eTRIKS tranSMART deployment * NOTE * ---- * This configuration assumes that the development environment will be used with * run-app and the production environment will be used with the application * packaged as a WAR and deployed to tomcat. Running grails run-war or otherwise * running a WAR with the development profile set up or activating the * production environment when running grails run-app are scenarios that have * NOT been tested. */ // eTRIKS tranSMART conf file 11-2016 // Core variables def catalinaBase = System.getProperty('catalina.base') ?: '.' def explodedWarDir = catalinaBase + '/webapps/transmart' def solrPort = 8983 def searchIndex = catalinaBase + '/searchIndex' def jobsDirectory = "/var/tmp/jobs" def oauthEnabled = false def samlEnabled = false def gwavaEnabled = false def LDAPEnabled = false def transmartURL = "http://localhost:${System.getProperty('server.port', '8080')}/transmart" // Personalization // Project name com.recomdata.projectName = "Test tranSMART Server" // name and URL of the supporter entity shown on the welcome page com.recomdata.providerName = "Bioinformatics Core @ LCSB" com.recomdata.providerURL = "https://uni.lu/lcsb" // Application logo com.recomdata.largeLogo = "transmartlogo.jpg" // Application search logo com.recomdata.smallLogo="transmartlogosmall.jpg" // Contact email com.recomdata.contactUs = "lcsb-sysadmins@uni.lu" // Application title com.recomdata.appTitle = "tranSMART v" + org.transmart.originalConfigBinding.appVersion // Location of the help pages com.recomdata.adminHelpURL = "$transmartURL/help/adminHelp/default.htm" // Bugreport URL com.recomdata.bugreportURL = "https://jira.transmartfoundation.org" // Email address of the administrator to contact com.recomdata.administrator="lcsb-sysadmins@uni.lu" // Site administrator contact email address com.recomdata.adminEmail = "lcsb-sysadmins@uni.lu" // Metadata view com.recomdata.view.studyview = 'studydetail' com.recomdata.plugins.resultSize = 5000 // Login and password policy // Session timeout and heartbeat frequency (ping interval) // Maximum concurrent sessions for a user (-1: unlimited) // org.transmartproject.maxConcurrentUserSessions = 10 // Not enabled by default (see Config-extra.php.sample) //com.recomdata.passwordstrength.pattern //com.recomdata.passwordstrength.description // Whether to enable guest auto login. // If it's enabled no login is required to access tranSMART. com.recomdata.guestAutoLogin = true environments { development { com.recomdata.guestAutoLogin = true } } // Guest account user name - if guestAutoLogin is true, this is the username of // the account that tranSMART will automatically authenticate users as. This will // control the level of access anonymous users will have (the access will match // that of the account specified here). com.recomdata.guestUserName = 'guest' bruteForceLoginLock { allowedNumberOfAttempts = 5 lockTimeInMinutes = 10 } // Password strength criteria, please change description accordingly //com.recomdata.passwordstrength.pattern = ~/^.*(?=.{8,})(?=.*[a-z])(?=.*[A-Z])(?=.*[\d])(?=.*[\W]).*$/ // Password strength regex that is used to test user's passwords that are entered by themselves. // Take care to change org.transmartproject.app.user.ChangePasswordCommand.newPassword.lowPasswordStrength // variable inside messages.properties file of web app. /* user.password.strength.regex = '''(?x) ^ (?=.*[A-Z]) #Ensure string has an uppercase letter. (?=.*[!@\#$&*]) #Ensure string has one special case letter. (?=.*[0-9]) #Ensure string has a digit. .{8,} #Ensure string length is not less then 8. $''' */ //Alternative color scheme for aCGH BED tracks. //Current colors are captured from cghCall R package. //Although do not correspond exactly. /* dataExport { bed { acgh { rgbColorScheme { //white invalid = [255, 255, 255] //red loss = [205, 0, 0] //dark normal = [ 10, 10, 10] //green gain = [ 0, 255, 0] //dark green amplification = [ 0, 100, 0] } } } } */ // Password strength description, please change according to pattern //com.recomdata.passwordstrength.description = // 'It should contain a minimum of 8 characters including at least ' + // '1 upper and 1 lower case letter, 1 digit and 1 special character.' // HTTP session variables environments { development { com.recomdata.sessionTimeout = Integer.MAX_VALUE / 1000 as int /* ~24 days */ com.recomdata.heartbeatLaps = 900 } production { if (com.recomdata.guestAutoLogin) { com.recomdata.sessionTimeout = Integer.MAX_VALUE / 1000 as int /* ~24 days */ com.recomdata.heartbeatLaps = 900 } else { com.recomdata.sessionTimeout = 3600 com.recomdata.heartbeatLaps = 30 } } } // User Interface setup ui { tabs { //Search was not part of 1.2. It's not working properly. You need to set `show` to `true` to see it on UI search.show = false browse.hide = true sampleExplorer.hide = false geneSignature.hide = true gwas.hide = true uploadData.hide = true datasetExplorer { gridView.hide = false dataExport.hide = true dataExportJobs.hide = true // Note: by default the analysisJobs panel is NOT shown // Currently, it is only used in special cases analysisJobs.show = false workspace.hide = false } } } // Variables checks environments { production { if (transmartURL.startsWith('http://localhost:')) { println "[WARN] transmartURL not overridden. Some settings (e.g. help page) may be wrong" } } } // Logs configuration import org.apache.log4j.DailyRollingFileAppender import org.transmart.logging.ChildProcessAppender import org.transmart.logging.JsonLayout log4j = { environments { development { root { info 'stdout' } // for a less verbose startup & shutdown warn 'org.codehaus.groovy.grails.commons.spring' warn 'org.codehaus.groovy.grails.orm.hibernate.cfg' warn 'org.codehaus.groovy.grails.domain.GrailsDomainClassCleaner' debug 'org.transmartproject' debug 'com.recomdata' debug 'grails.app.controllers' debug 'grails.app.services' debug 'grails.app.domain' debug 'grails.app.services.com.recomdata' debug 'grails.app.services.org.transmartproject' debug 'grails.app.controllers.com.recomdata' debug 'grails.app.controllers.org.transmartproject' debug 'grails.app.domain.com.recomdata' debug 'grails.app.domain.org.transmartproject' // debug 'org.springframework.security' // (very verbose) debug 'org.grails.plugin.resource' } production { def logDirectory = "${catalinaBase}/logs".toString() appenders { rollingFile(name: 'transmart', file: "${logDirectory}/transmart.log", layout: pattern(conversionPattern: '%d{dd-MM-yyyy HH:mm:ss,SSS} %5p %c{1} - %m%n'), maxFileSize: '100MB') appender new DailyRollingFileAppender( name: 'fileAuditLogger', datePattern: "'.'yyyy-MM-dd", fileName: "${logDirectory}/audit.log", layout: pattern(conversionPattern: '%d{dd-MM-yyyy HH:mm:ss,SSS} %5p %c{1} - %m%n') ) } root { warn 'transmart' } trace additivity: false, fileAuditLogger: 'org.transmart.audit' trace transmart: 'org.transmart.audit' debug 'org.transmartproject' debug 'com.recomdata' debug 'grails.app.services.com.recomdata' debug 'grails.app.services' debug 'grails.app.services.transmartapp' debug 'grails.app.services.org.transmartproject' debug 'grails.app.controllers.com.recomdata' debug 'grails.app.controllers.org.transmartproject' debug 'grails.app.domain.com.recomdata' debug 'grails.app.domain.org.transmartproject' } } } // Solr configuration environments { development { com.rwg.solr.scheme = 'http' com.rwg.solr.host = 'localhost:8983' com.rwg.solr.path = '/solr/rwg/select/' } production { com.rwg.solr.scheme = 'http' com.rwg.solr.host = 'localhost:' + solrPort com.rwg.solr.path = '/solr/rwg/select/' } } com.rwg.solr.browse.path = '/solr/browse/select/' com.rwg.solr.update.path = '/solr/browse/dataimport/' com.recomdata.solr.baseURL = "${com.rwg.solr.scheme}://${com.rwg.solr.host}" + "${new File(com.rwg.solr.browse.path).parent}" def fileStoreDirectory = new File(System.getenv('HOME'), '.grails/transmart-filestore') def fileImportDirectory = new File(System.getProperty("java.io.tmpdir"), 'transmart-fileimport') com.recomdata.FmFolderService.filestoreDirectory = fileStoreDirectory.absolutePath com.recomdata.FmFolderService.importDirectory = fileImportDirectory.absolutePath [fileStoreDirectory, fileImportDirectory].each { if (!it.exists()) { it.mkdir() } } // Search tool configuration // Lucene index location for documentation search com.recomdata.searchengine.index = searchIndex // Sample Explorer configuration // This is an object to dictate the names and 'pretty names' of the SOLR fields. // Optionally you can set the width of each of the columns when rendered. sampleExplorer { fieldMapping = [ columns:[ [header:'ID', dataIndex:'id', mainTerm: true, showInGrid: true, width:20], [header:'trial name', dataIndex:'trial_name', mainTerm: true, showInGrid: true, width:20], [header:'barcode', dataIndex:'barcode', mainTerm: true, showInGrid: true, width:20], [header:'plate id', dataIndex:'plate_id', mainTerm: true, showInGrid: true, width:20], [header:'patient id', dataIndex:'patient_id', mainTerm: true, showInGrid: true, width:20], [header:'external id', dataIndex:'external_id', mainTerm: true, showInGrid: true, width:20], [header:'aliquot id', dataIndex:'aliquot_id', mainTerm: true, showInGrid: true, width:20], [header:'visit', dataIndex:'visit', mainTerm: true, showInGrid: true, width:20], [header:'sample type', dataIndex:'sample_type', mainTerm: true, showInGrid: true, width:20], [header:'description', dataIndex:'description', mainTerm: true, showInGrid: true, width:20], [header:'comment', dataIndex:'comment', mainTerm: true, showInGrid: true, width:20], [header:'location', dataIndex:'location', mainTerm: true, showInGrid: true, width:20], [header:'organism', dataIndex:'source_organism', mainTerm: true, showInGrid: true, width:20] ] ] resultsGridHeight = 100 resultsGridWidth = 100 idfield = 'id' } edu.harvard.transmart.sampleBreakdownMap = [ "aliquot_id":"Aliquots in Cohort" ] // Solr configuration for the Sample Explorer com { recomdata { solr { maxNewsStories = 10 maxRows = 10000 }}} // Dataset Explorer configuration com { recomdata { datasetExplorer { // set to 'true' (quotes included) to enable gene pattern integration genePatternEnabled = 'false' // The tomcat URL that gene pattern is deployed within -usually it's proxyed through apache genePatternURL = 'http://localhost' // Gene Pattern real URL with port number genePatternRealURLBehindProxy = 'http://localhost' // default Gene pattern user to start up - each tranSMART user will need a separate user account to be created in Gene Pattern genePatternUser = 'biomart' // Absolute path to PLINK executables plinkExcutable = '/usr/local/bin/plink' } } } // RModules & Data Export Configuration environments { // This is to target a remote Rserve. Bear in mind the need for shared network storage RModules.host = "127.0.0.1" RModules.port = 6340 // This is not used in recent versions; the URL is always /analysisFiles/ RModules.imageURL = "/tempImages/" production { // The working directory for R scripts, where the jobs get created and // output files get generated RModules.tempFolderDirectory = jobsDirectory RModules.transferImageFile = false } development { RModules.tempFolderDirectory = "/tmp" /* we don't need to specify temporaryImageDirectory, because we're not copying */ RModules.transferImageFile = false } // Used to access R jobs parent directory outside RModules (e.g. data export) com.recomdata.plugins.tempFolderDirectory = RModules.tempFolderDirectory } // GWAS Configuration com.recomdata.dataUpload.appTitle="Upload data to tranSMART" com.recomdata.dataUpload.stageScript="run_analysis_stage" // Directory path of com.recomdata.dataUpload.stageScript def gwasEtlDirectory = new File(System.getenv('HOME'), '.grails/transmart-gwasetl') // Directory to hold GWAS file uploads def gwasUploadsDirectory = new File(System.getenv('HOME'), '.grails/transmart-datauploads') // Directory to preload with template files with names -template.txt def gwasTemplatesDirectory = new File(System.getenv('HOME'), '.grails/transmart-templates') com.recomdata.dataUpload.templates.dir = gwasTemplatesDirectory.absolutePath com.recomdata.dataUpload.uploads.dir = gwasUploadsDirectory.absolutePath com.recomdata.dataUpload.etl.dir = gwasEtlDirectory.absolutePath [gwasTemplatesDirectory, gwasUploadsDirectory, gwasEtlDirectory].each { if (!it.exists()) { it.mkdir() } } // Misc Configuration // This can be used to debug JavaScript callbacks in the dataset explorer in // Chrome. Unfortunately, it also sometimes causes chrome to segfault com.recomdata.debug.jsCallbacks = 'false' environments { production { com.recomdata.debug.jsCallbacks = 'false' } } grails.resources.adhoc.excludes = [ '/images' + RModules.imageURL + '**' ] // Adding properties to the Build information panel buildInfo { properties { include = [ 'app.grails.version', 'build.groovy' ] exclude = [ 'env.proc.cores' ] } } // Spring Security configuration grails { plugin { springsecurity { // customized user GORM class userLookup.userDomainClassName = 'org.transmart.searchapp.AuthUser' // customized password field userLookup.passwordPropertyName = 'passwd' // customized user /role join GORM class userLookup.authorityJoinClassName = 'org.transmart.searchapp.AuthUser' // customized role GORM class authority.className = 'org.transmart.searchapp.Role' // request map GORM class name - request map is stored in the db requestMap.className = 'org.transmart.searchapp.Requestmap' // requestmap in db securityConfigType = grails.plugin.springsecurity.SecurityConfigType.Requestmap // url to redirect after login in // just_rest branch provides alternative default via org.transmart.defaultLoginRedirect successHandler.defaultTargetUrl = org.transmart.defaultLoginRedirect ?: '/userLanding' // logout url logout.afterLogoutUrl = '/login/forceAuth' // configurable requestmap functionality in transmart is deprecated def useRequestMap = false if (useRequestMap) { // requestmap in db securityConfigType = 'Requestmap' // request map GORM class name - request map is stored in the db requestMap.className = 'org.transmart.searchapp.Requestmap' } else { securityConfigType = 'InterceptUrlMap' def oauthEndpoints = [ '/oauth/authorize.dispatch': ["isFullyAuthenticated() and (request.getMethod().equals('GET') or request.getMethod().equals('POST'))"], '/oauth/token.dispatch': ["isFullyAuthenticated() and request.getMethod().equals('POST')"], ] // This looks dangerous and it possibly is (would need to check), but // reflects the instructions I got from the developer. def gwavaMappings = [ '/gwasWeb/**' : ['IS_AUTHENTICATED_ANONYMOUSLY'], ] interceptUrlMap = [ '/login/**' : ['IS_AUTHENTICATED_ANONYMOUSLY'], '/css/**' : ['IS_AUTHENTICATED_ANONYMOUSLY'], '/js/**' : ['IS_AUTHENTICATED_ANONYMOUSLY'], '/grails-errorhandler' : ['IS_AUTHENTICATED_ANONYMOUSLY'], '/images/analysisFiles/**' : ['IS_AUTHENTICATED_REMEMBERED'], '/images/**' : ['IS_AUTHENTICATED_ANONYMOUSLY'], '/static/**' : ['IS_AUTHENTICATED_ANONYMOUSLY'], '/search/loadAJAX**' : ['IS_AUTHENTICATED_ANONYMOUSLY'], '/analysis/getGenePatternFile': ['IS_AUTHENTICATED_ANONYMOUSLY'], '/analysis/getTestFile' : ['IS_AUTHENTICATED_ANONYMOUSLY'], '/requestmap/**' : ['ROLE_ADMIN'], '/role/**' : ['ROLE_ADMIN'], '/authUser/**' : ['ROLE_ADMIN'], '/secureObject/**' : ['ROLE_ADMIN'], '/accessLog/**' : ['ROLE_ADMIN'], '/authUserSecureAccess/**' : ['ROLE_ADMIN'], '/secureObjectPath/**' : ['ROLE_ADMIN'], '/userGroup/**' : ['ROLE_ADMIN'], '/secureObjectAccess/**' : ['ROLE_ADMIN'], '/oauthAdmin/**' : ['ROLE_ADMIN'], * : (oauthEnabled ? oauthEndpoints : [:]), * : (gwavaEnabled ? gwavaMappings : [:]), '/**' : ['IS_AUTHENTICATED_REMEMBERED'], // must be last ] rejectIfNoRule = true } // Password hash algorithm //password.algorithm = 'bcrypt' // Number of bcrypt rounds //password.bcrypt.logrounds = 14 // Password parameters used with LDAP password.algorithm = 'SHA-1' password.hash.iterations = 1 // Spring security – error messages errors.login.expired = 'Your account has expired' errors.login.passwordExpired = 'Your password has expired' errors.login.disabled = 'Your login has been disabled' errors.login.locked = 'Your account has been locked' errors.login.fail = 'Login has failed; check the provided credentials' // Authentication providers (LDAP and Guest mainly) providerNames = ['daoAuthenticationProvider', 'anonymousAuthenticationProvider', 'rememberMeAuthenticationProvider', ] // OAuth check if (oauthEnabled) { providerNames << 'clientCredentialsAuthenticationProvider' def securedResourcesFilters = [ 'JOINED_FILTERS', '-securityContextPersistenceFilter', '-logoutFilter', '-rememberMeAuthenticationFilter', '-basicAuthenticationFilter', '-exceptionTranslationFilter', ].join(',') filterChain.chainMap = [ '/oauth/token': [ 'JOINED_FILTERS', '-oauth2ProviderFilter', '-securityContextPersistenceFilter', '-logoutFilter', '-rememberMeAuthenticationFilter', '-exceptionTranslationFilter', ].join(','), '/studies/**': securedResourcesFilters, '/observations/**': securedResourcesFilters, '/patient_sets/**': securedResourcesFilters, '/oauth/inspectToken': securedResourcesFilters, '/**': [ 'JOINED_FILTERS', '-statelessSecurityContextPersistenceFilter', '-oauth2ProviderFilter', '-clientCredentialsTokenEndpointFilter', '-basicAuthenticationFilter', '-oauth2ExceptionTranslationFilter' ].join(','), ] grails.exceptionresolver.params.exclude = ['password', 'client_secret'] def glowingBearRedirectUris = [ transmartURL - ~/transmart\/?$/ + 'connections', ] // for dev, node reverse proxy runs on 8001 glowingBearRedirectUris << 'http://localhost:8001/connections' oauthProvider { authorization.requireRegisteredRedirectUri = true authorization.requireScope = false clients = [ [ clientId: 'api-client', clientSecret: 'api-client', authorities: ['ROLE_CLIENT'], scopes: ['read', 'write'], authorizedGrantTypes: ['authorization_code', 'refresh_token'], redirectUris: [(transmartURL - ~'\\/$') + '/oauth/verify'], ], [ clientId: 'glowingbear-js', clientSecret: '', authorities: ['ROLE_CLIENT'], scopes: ['read', 'write'], authorizedGrantTypes: ['implicit', 'password'], redirectUris: glowingBearRedirectUris, ], ] } } // LDAP check if (LDAPEnabled) { providerNames << 'ldapAuthProvider' } } } } // LDAP configuration if (LDAPEnabled) { org.transmart.security.spnegoEnabled = true grails { plugin { springsecurity { ldap { active = true context { // Address of the LDAP server server = 'ldap://10.240.6.51' // Distinguished Name (DN) to authenticate with managerDn = 'uid=admin,cn=users,cn=accounts,dc=novalocal,dc=novalocal' // Password to authenticate with managerPassword = 'secret' // Whether or not integrate internal roles. Blurry notion, documentation missing allowInternaRoles = 'false' } search { // Context name to search in, relative to the base of the configured ContextSource, e.g. 'dc=example,dc=com', 'ou=users,dc=example,dc=com' base = 'cn=users,cn=accounts,dc=novalocal,dc=novalocal' // The filter expression used in the user search filter = '(uid={0})' } // Names of attribute ids to return; use null to return all and an empty list to return none authenticator.attributesToReturn = ['uid', 'mail', 'cn'] authorities { // The base DN from which the search for group membership should be performed groupSearchBase = 'ou=transmart_vm1,ou=project_1,ou=transmart,dc=novalocal,dc=novalocal' // The pattern to be used for the user search. {0} is the user's DN groupSearchFilter = 'memberUid={1}' // Whether PartialResultExceptions should be ignored in searches, typically used with Active Directory since AD servers often have a problem with referrals ignorePartialResultException = true // Whether to infer roles based on group membership retrieveGroupRoles = true // Whether to retrieve additional roles from the database using the User/Role many-to-many retrieveDatabaseRoles = false } } } } } } // SAML Configuration if (samlEnabled) { // don't do assignment to grails.plugin.springsecurity.providerNames // see GRAILS-11730 grails { plugin { springsecurity { providerNames << 'samlAuthenticationProvider' } } } // again, because of GRAILS-11730 def ourPluginConfig grails { ourPluginConfig = plugin } org { transmart { security { setProperty('samlEnabled', true) // Clashed with local variable ssoEnabled = "true" // URL to redirect to after successful authentication successRedirectHandler.defaultTargetUrl = ourPluginConfig.springsecurity.successHandler.defaultTargetUrl // URL to redirect to after successful logout successLogoutHandler.defaultTargetUrl = ourPluginConfig.springsecurity.logout.afterLogoutUrl saml { // Service provider details (we) sp { // ID of the Service Provider id = "gustavo-transmart" // URL of the service provider. This should be autodected, but it isn't url = "http://localhost:8080/transmart" // Alias of the Service Provider alias = "transmart" // Alias of the Service Provider's signing key, see keystore details signingKeyAlias = "saml-signing" // Alias of the Service Provider's encryption key encryptionKeyAlias = "saml-encryption" } // Metadata file of the provider. We insist on keeping instead of just // retrieving it from the provider on startup to prevent transmart from // being unable to start due to provider being down. A copy will still be // periodically fetched from the provider idp.metadataFile = '/home/glopes/idp-local-metadata.xml' // Keystore details keystore { // Generate with: // keytool -genkey -keyalg RSA -alias saml-{signing,encryption} \ // -keystore transmart.jks -storepass changeit \ // -validity 3602 -keysize 2048 // Location of the keystore. You can use other schemes, like classpath:resource/samlKeystore.jks file = 'file:///home/glopes/transmart.jks' // keystore's storepass password="changeit" // keystore's default key defaultKey="saml-signing" // Alias of the encryption key in the keystore encryptionKey.alias="saml-encryption" // Password of the key with above alias in the keystore encryptionKey.password="changeit" // Alias of the signing key in the keystore signingKey.alias="saml-signing" // Password of the key with above alias in the keystore signingKey.password="changeit" } // Creation of new users createInexistentUsers = "true" attribute.username = "urn:custodix:ciam:1.0:principal:username" attribute.firstName = "urn:oid:2.5.4.42" attribute.lastName = "urn:oid:2.5.4.4" attribute.email = "" attribute.federatedId = "personPrincipalName" // Suffix of the login filter, saml authentication is initiated when user browses to this url entryPoint.filterProcesses = "/saml/login" // SAML Binding to be used for above entry point url. entryPoint.binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" // This property must be set otherwise the default binding is used which, in this configuration, is HTTP-ARTIFACT entryPoint.defaultAssertionConsumerIndex = "1" // Suffix of the Service Provider's metadata, this url needs to be configured on IDP metadata.filterSuffix = "/saml/metadata" // Id of the spring security's authentication manager authenticationManager = "authenticationManager" // Whether sessions should be invalidated after logout logout.invalidateHttpSession = "true" // Id of the spring security user service that should be called to fetch users. saml.userService = "org.transmart.FederatedUserDetailsService" } } } } } else { // if (!samlEnabled) org { transmart { security { setProperty('samlEnabled', false) // Clashed with local variable } } } } // GWAVA configuration if (gwavaEnabled) { // assume deployment alongside transmart com { recomdata { rwg { webstart { def url = new URL(transmartURL) codebase = "$url.protocol://$url.host${url.port != -1 ? ":$url.port" : ''}/gwava" jar = './ManhattanViz2.1g.jar' mainClass = 'com.pfizer.mrbt.genomics.Driver' gwavaInstance = 'transmartstg' transmart.url = transmartURL - ~'\\/$' } } } } com { recomdata { rwg { qqplots { cacheImages = new File(jobsDirectory, 'cachedQQplotImages').toString() } } } } } // Quartz jobs configuration // start delay for the sweep job com.recomdata.export.jobs.sweep.startDelay = 60000 // d*h*m*s*1000 // repeat interval for the sweep job com.recomdata.export.jobs.sweep.repeatInterval = 86400000 // d*h*m*s*1000 // specify the age of files to be deleted (in days) com.recomdata.export.jobs.sweep.fileAge = 3 // EOF You MUST leave this at the end org.transmart.configFine = true // vim: set fdm=marker et ts=4 sw=4 filetype=groovy ai: