diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index 7e32fa444b6..57ca628fc14 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -42,10 +42,12 @@ jobs: working-directory: ./cbioportal-docker-compose run: | cd ./data && ./init.sh && rm -rf ./studies/* && cd ../config && \ - cat $PORTAL_SOURCE_DIR/portal/target/portal/WEB-INF/classes/portal.properties | \ - sed 's/db.host=.*/db.host=cbioportal-database:3306/g' | \ - sed 's|db.connection_string=.*|db.connection_string=jdbc:mysql://cbioportal-database:3306/|g' \ - > portal.properties + cat $PORTAL_SOURCE_DIR/portal/target/classes/portal.properties | \ + sed 's|db.host=.*||' | \ + sed 's|db.portal_db_name=.*||' | \ + sed 's|db.use_ssl=.*||' | \ + sed 's|db.connection_string=.*|db.connection_string=jdbc:mysql://cbioportal-database:3306/cbioportal?useSSL=false\&allowPublicKeyRetrieval=true|' \ + > portal.properties && more portal.properties - name: 'Start cbioportal-docker-compose' working-directory: ./cbioportal-docker-compose run: | diff --git a/core/src/main/java/org/mskcc/cbio/portal/dao/JdbcDataSource.java b/core/src/main/java/org/mskcc/cbio/portal/dao/JdbcDataSource.java index 5b9d0548669..00bcb76b7cb 100644 --- a/core/src/main/java/org/mskcc/cbio/portal/dao/JdbcDataSource.java +++ b/core/src/main/java/org/mskcc/cbio/portal/dao/JdbcDataSource.java @@ -3,28 +3,44 @@ import org.apache.commons.dbcp2.BasicDataSource; import org.mskcc.cbio.portal.util.DatabaseProperties; import org.apache.commons.lang3.StringUtils; +import org.springframework.util.Assert; /** * Data source that self-initializes based on cBioPortal configuration. */ public class JdbcDataSource extends BasicDataSource { + public JdbcDataSource () { DatabaseProperties dbProperties = DatabaseProperties.getInstance(); + String host = dbProperties.getDbHost(); String userName = dbProperties.getDbUser(); String password = dbProperties.getDbPassword(); String mysqlDriverClassName = dbProperties.getDbDriverClassName(); String database = dbProperties.getDbName(); - String useSSL = (!StringUtils.isBlank(dbProperties.getDbUseSSL())) ? dbProperties.getDbUseSSL() : "false"; String enablePooling = (!StringUtils.isBlank(dbProperties.getDbEnablePooling())) ? dbProperties.getDbEnablePooling(): "false"; - String url ="jdbc:mysql://" + host + "/" + database + - "?user=" + userName + "&password=" + password + - "&zeroDateTimeBehavior=convertToNull&useSSL=" + useSSL; + String connectionURL = dbProperties.getConnectionURL(); + + Assert.isTrue( + !defined(host) && !defined(database) && !defined(dbProperties.getDbUseSSL()), + "\n----------------------------------------------------------------------------------------------------------------" + + "-- Connection error:\n" + + "-- You try to connect to the database using the deprecated 'db.host', 'db.portal_db_name' and 'db.use_ssl' properties.\n" + + "-- Please remove these properties and use the 'db.connection_string' property instead. See https://docs.cbioportal.org/deployment/customization/portal.properties-reference/\n" + + "-- for assistance on building a valid connection string.\n" + + "----------------------------------------------------------------------------------------------------------------\n" + ); + + Assert.hasText(userName, errorMessage("username", "db.user")); + Assert.hasText(password, errorMessage("password", "db.password")); + Assert.hasText(mysqlDriverClassName, errorMessage("driver class name", "db.driver")); + + this.setUrl(connectionURL); + // Set up poolable data source this.setDriverClassName(mysqlDriverClassName); this.setUsername(userName); this.setPassword(password); - this.setUrl(url); // Disable this to avoid caching statements this.setPoolPreparedStatements(Boolean.valueOf(enablePooling)); // these are the values cbioportal has been using in their production @@ -37,4 +53,12 @@ public JdbcDataSource () { this.setValidationQuery("SELECT 1"); this.setJmxName("org.cbioportal:DataSource=" + database); } + + private String errorMessage(String displayName, String propertyName) { + return String.format("No %s provided for database connection. Please set '%s' in portal.properties.", displayName, propertyName); + } + + private boolean defined(String property) { + return property != null && !property.isEmpty(); + } } diff --git a/core/src/main/java/org/mskcc/cbio/portal/util/DatabaseProperties.java b/core/src/main/java/org/mskcc/cbio/portal/util/DatabaseProperties.java index fe19d5b538f..e2ec2753bec 100644 --- a/core/src/main/java/org/mskcc/cbio/portal/util/DatabaseProperties.java +++ b/core/src/main/java/org/mskcc/cbio/portal/util/DatabaseProperties.java @@ -47,6 +47,7 @@ public class DatabaseProperties { private String dbDriverClassName; private String dbUseSSL; private String dbEnablePooling; + private String connectionURL; // No production keys stored in filesystem or code: digest the key; put it in properties; load it into dbms on startup private static DatabaseProperties dbProperties; @@ -63,6 +64,7 @@ public static DatabaseProperties getInstance() { dbProperties.setDbDriverClassName(GlobalProperties.getProperty("db.driver")); dbProperties.setDbUseSSL(GlobalProperties.getProperty("db.use_ssl")); dbProperties.setDbEnablePooling(GlobalProperties.getProperty("db.enable_pooling")); + dbProperties.setConnectionURL(GlobalProperties.getProperty("db.connection_string")); } return dbProperties; } @@ -134,4 +136,12 @@ public void setDbEnablePooling(String dbEnablePooling) { this.dbEnablePooling = dbEnablePooling; } + public String getConnectionURL() { + return connectionURL; + } + + public void setConnectionURL(String connectionURL) { + this.connectionURL = connectionURL; + } + } diff --git a/core/src/main/java/org/mskcc/cbio/portal/util/SpringUtil.java b/core/src/main/java/org/mskcc/cbio/portal/util/SpringUtil.java index 15e3dc0e95c..f8e2d57976f 100644 --- a/core/src/main/java/org/mskcc/cbio/portal/util/SpringUtil.java +++ b/core/src/main/java/org/mskcc/cbio/portal/util/SpringUtil.java @@ -38,17 +38,14 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.context.support.GenericXmlApplicationContext; import org.springframework.stereotype.Component; @Component -public class SpringUtil -{ +public class SpringUtil { private static final Logger log = LoggerFactory.getLogger(SpringUtil.class); private static AccessControl accessControl; - private static ApplicationContext context; - private static GenericXmlApplicationContext applicationContext; + private static ApplicationContext applicationContext; @Autowired public void setAccessControl(AccessControl accessControl) { @@ -56,15 +53,13 @@ public void setAccessControl(AccessControl accessControl) { SpringUtil.accessControl = accessControl; } - public static AccessControl getAccessControl() - { + public static AccessControl getAccessControl() { return accessControl; } - public static synchronized void initDataSource() - { - if (SpringUtil.context == null) { - context = new ClassPathXmlApplicationContext("classpath:applicationContext-persistenceConnections.xml"); + public static synchronized void initDataSource() { + if (SpringUtil.applicationContext == null) { + SpringUtil.applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext-persistenceConnections.xml"); } } @@ -74,7 +69,7 @@ public static synchronized void initDataSource() * @return the Spring Framework application context */ public static ApplicationContext getApplicationContext() { - return context; + return applicationContext; } /** @@ -84,7 +79,7 @@ public static ApplicationContext getApplicationContext() { * @param context */ public static void setApplicationContext(ApplicationContext context) { - SpringUtil.context = context; + SpringUtil.applicationContext = context; } /** @@ -93,8 +88,7 @@ public static void setApplicationContext(ApplicationContext context) { * * @param context */ - public static synchronized void initDataSource(ApplicationContext context) - { - SpringUtil.context = context; + public static synchronized void initDataSource(ApplicationContext context) { + SpringUtil.applicationContext = context; } } diff --git a/core/src/main/scripts/importer/cbioportalImporter.py b/core/src/main/scripts/importer/cbioportalImporter.py index a40e9dc4525..2e0d812a191 100755 --- a/core/src/main/scripts/importer/cbioportalImporter.py +++ b/core/src/main/scripts/importer/cbioportalImporter.py @@ -419,8 +419,7 @@ def usage(): '--command [%s] --study_directory ' '--meta_filename ' '--data_filename ' - '--study_ids ' - '--properties-filename ' % (COMMANDS)), file=OUTPUT_FILE) + '--study_ids ' % (COMMANDS)), file=OUTPUT_FILE) def check_args(command): if command not in COMMANDS: diff --git a/core/src/main/scripts/importer/cbioportal_common.py b/core/src/main/scripts/importer/cbioportal_common.py index 3a6355eb07f..71428f1dcb2 100644 --- a/core/src/main/scripts/importer/cbioportal_common.py +++ b/core/src/main/scripts/importer/cbioportal_common.py @@ -11,7 +11,9 @@ import logging.handlers from collections import OrderedDict from subprocess import Popen, PIPE, STDOUT - +from typing import Dict, Optional +import dsnparse +import MySQLdb # ------------------------------------------------------------------------------ # globals @@ -37,6 +39,14 @@ ADD_CASE_LIST_CLASS = "org.mskcc.cbio.portal.scripts.AddCaseList" VERSION_UTIL_CLASS = "org.mskcc.cbio.portal.util.VersionUtil" +PORTAL_PROPERTY_DATABASE_USER = 'db.user' +PORTAL_PROPERTY_DATABASE_PW = 'db.password' +PORTAL_PROPERTY_DATABASE_HOST = 'db.host' +PORTAL_PROPERTY_DATABASE_NAME = 'db.portal_db_name' +PORTAL_PROPERTY_DATABASE_URL = 'db.connection_string' +PORTAL_PROPERTY_DATABASE_USESSL = 'db.use_ssl' +REQUIRED_DATABASE_PROPERTIES = [PORTAL_PROPERTY_DATABASE_USER, PORTAL_PROPERTY_DATABASE_PW, PORTAL_PROPERTY_DATABASE_URL] + # provides a key for data types to metafile specification dict. class MetaFileTypes(object): """how we differentiate between data types.""" @@ -1000,3 +1010,96 @@ def run_java(*args): elif process.returncode != 0: raise RuntimeError('Aborting due to error while executing step.') return ret + + +def properties_error_message(display_name: str, property_name: str) -> str: + return f"No {display_name} provided for database connection. Please set '{property_name}' in portal.properties." + + +class PortalProperties(object): + """ Properties object class, just has fields for db conn """ + + def __init__(self, database_user, database_pw, database_url): + self.database_user = database_user + self.database_pw = database_pw + self.database_url = database_url + + +def get_database_properties(properties_filename: str) -> Optional[PortalProperties]: + + properties = parse_properties_file(properties_filename) + + missing_properties = [] + for required_property in REQUIRED_DATABASE_PROPERTIES: + if required_property not in properties or len(properties[required_property]) == 0: + missing_properties.append(required_property) + if missing_properties: + print( + 'Missing required properties : (%s)' % (', '.join(missing_properties)), + file=ERROR_FILE) + return None + + if properties.get(PORTAL_PROPERTY_DATABASE_HOST) is not None \ + or properties.get(PORTAL_PROPERTY_DATABASE_NAME) is not None \ + or properties.get(PORTAL_PROPERTY_DATABASE_USESSL) is not None: + print(""" + ---------------------------------------------------------------------------------------------------------------- + -- Connection error: + -- You try to connect to the database using the deprecated 'db.host', 'db.portal_db_name' and 'db.use_ssl' properties. + -- Please remove these properties and use the 'db.connection_string' property instead. See https://docs.cbioportal.org/deployment/customization/portal.properties-reference/ + -- for assistance on building a valid connection string. + ------------------------------------------------------------f--------------------------------------------------- + """, file=ERROR_FILE) + return None + + return PortalProperties(properties[PORTAL_PROPERTY_DATABASE_USER], + properties[PORTAL_PROPERTY_DATABASE_PW], + properties[PORTAL_PROPERTY_DATABASE_URL]) + + +def parse_properties_file(properties_filename: str) -> Dict[str, str]: + + if not os.path.exists(properties_filename): + print('properties file %s cannot be found' % properties_filename, file=ERROR_FILE) + sys.exit(2) + + properties = {} + with open(properties_filename, 'r') as properties_file: + for line in properties_file: + line = line.strip() + # skip line if its blank or a comment + if len(line) == 0 or line.startswith('#'): + continue + try: + name, value = line.split('=', maxsplit=1) + except ValueError: + print( + 'Skipping invalid entry in property file: %s' % line, + file=ERROR_FILE) + continue + properties[name] = value.strip() + return properties + + +def get_db_cursor(portal_properties: PortalProperties): + + try: + url_elements = dsnparse.parse(portal_properties.database_url) + connection_kwargs = { + "host": url_elements.host, + "port": url_elements.port if url_elements.port is not None else 3306, + "db": url_elements.paths[0], + "user": portal_properties.database_user, + "passwd": portal_properties.database_pw, + "ssl": "useSSL" not in url_elements.query or url_elements.query["useSSL"] == "true" + } + connection = MySQLdb.connect(**connection_kwargs) + except MySQLdb.Error as exception: + print(exception, file=ERROR_FILE) + message = ( + "--> Error connecting to server with URL" + + portal_properties.database_url) + print(message, file=ERROR_FILE) + raise ConnectionError(message) from exception + if connection is not None: + return connection, connection.cursor() diff --git a/core/src/main/scripts/importer/updateOncokbAnnotations.py b/core/src/main/scripts/importer/updateOncokbAnnotations.py index 2db94d9eb35..df7799387ca 100644 --- a/core/src/main/scripts/importer/updateOncokbAnnotations.py +++ b/core/src/main/scripts/importer/updateOncokbAnnotations.py @@ -29,10 +29,12 @@ import argparse import importlib import logging.handlers -import os import sys import MySQLdb from pathlib import Path +from cbioportal_common import get_database_properties, get_db_cursor +import libImportOncokb + # configure relative imports if running as a script; see PEP 366 # it might passed as empty string by certain tooling to mark a top level module @@ -46,16 +48,8 @@ # doesn't include https://github.com/python/cpython/pull/2639 importlib.import_module(__package__) -from . import cbioportal_common -from . import libImportOncokb -from . import validateData ERROR_FILE = sys.stderr -DATABASE_HOST = 'db.host' -DATABASE_NAME = 'db.portal_db_name' -DATABASE_USER = 'db.user' -DATABASE_PW = 'db.password' -REQUIRED_PROPERTIES = [DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PW] REFERENCE_GENOME = {'hg19': 'GRCh37', 'hg38': 'GRCh38'} # from: cbioportal-frontend file CopyNumberUtils.ts @@ -66,84 +60,6 @@ "AMPLIFICATION": 2, } -class PortalProperties(object): - """ Properties object class, just has fields for db conn """ - - def __init__(self, database_host, database_name, database_user, database_pw): - # default port: - self.database_port = 3306 - # if there is a port added to the host name, split and use this one: - if ':' in database_host: - host_and_port = database_host.split(':') - self.database_host = host_and_port[0] - if self.database_host.strip() == 'localhost': - print( - "Invalid host config '" + database_host + "' in properties file. If you want to specify a port on local host use '127.0.0.1' instead of 'localhost'", - file=ERROR_FILE) - sys.exit(1) - self.database_port = int(host_and_port[1]) - else: - self.database_host = database_host - self.database_name = database_name - self.database_user = database_user - self.database_pw = database_pw - -def get_portal_properties(properties_filename): - """ Returns a properties object """ - properties = {} - with open(properties_filename, 'r') as properties_file: - for line in properties_file: - line = line.strip() - # skip line if its blank or a comment - if len(line) == 0 or line.startswith('#'): - continue - try: - name, value = line.split('=', maxsplit=1) - except ValueError: - print( - 'Skipping invalid entry in property file: %s' % (line), - file=ERROR_FILE) - continue - properties[name] = value.strip() - missing_properties = [] - for required_property in REQUIRED_PROPERTIES: - if required_property not in properties or len(properties[required_property]) == 0: - missing_properties.append(required_property) - if missing_properties: - print( - 'Missing required properties : (%s)' % (', '.join(missing_properties)), - file=ERROR_FILE) - return None - # return an instance of PortalProperties - return PortalProperties(properties[DATABASE_HOST], - properties[DATABASE_NAME], - properties[DATABASE_USER], - properties[DATABASE_PW]) - -def get_db_cursor(portal_properties): - """ Establishes a MySQL connection """ - try: - connection = MySQLdb.connect(host=portal_properties.database_host, - port = portal_properties.database_port, - user = portal_properties.database_user, - passwd = portal_properties.database_pw, - db = portal_properties.database_name) - connection.autocommit = False - except MySQLdb.Error as exception: - print(exception, file=ERROR_FILE) - port_info = '' - if portal_properties.database_host.strip() != 'localhost': - # only add port info if host is != localhost (since with localhost apparently sockets are used and not the given port) TODO - perhaps this applies for all names vs ips? - port_info = " on port " + str(portal_properties.database_port) - message = ( - "--> Error connecting to server " - + portal_properties.database_host - + port_info) - print(message, file=ERROR_FILE) - raise ConnectionError(message) from exception - if connection is not None: - return connection, connection.cursor() - def get_current_mutation_data(study_id, cursor): """ Get mutation data from the current study. Returns an array of dictionaries, with the following keys: @@ -306,41 +222,36 @@ def update_annotations(result, connection, cursor, study_id): except MySQLdb.Error as msg: print(msg, file=ERROR_FILE) -def main_import(study_id, properties_filename): - # check existence of properties file - if not os.path.exists(properties_filename): - print('properties file %s cannot be found' % (properties_filename), file=ERROR_FILE) - sys.exit(2) - - # parse properties file - portal_properties = get_portal_properties(properties_filename) +def main_import(study_id, properties_filename): + + portal_properties = get_database_properties(properties_filename) if portal_properties is None: - print('failure reading properties file (%s)' % (properties_filename), file=ERROR_FILE) + print('failure reading properties file (%s)' % properties_filename, file=ERROR_FILE) sys.exit(1) - # Connect to the database + # Connect to the database. # TODO: must be a unique transaction connection, cursor = get_db_cursor(portal_properties) if cursor is None: print('failure connecting to sql database', file=ERROR_FILE) sys.exit(1) - #Query DB to get mutation, cna and structural variant data of the study + # Query DB to get mutation, cna and structural variant data of the study. mutation_study_data = get_current_mutation_data(study_id, cursor) cna_study_data = get_current_cna_data(study_id, cursor) sv_study_data = get_current_sv_data(study_id, cursor) - #Call oncokb to get annotations for the mutation, cna and structural variant data retrieved + # Call OncoKB to get annotations for the mutation, cna and structural variant data retrieved. ref_genome = get_reference_genome(study_id, cursor) mutation_result = fetch_oncokb_mutation_annotations(mutation_study_data, ref_genome) cna_result = fetch_oncokb_copy_number_annotations(cna_study_data, ref_genome) sv_result = fetch_oncokb_sv_annotations(sv_study_data, ref_genome) all_results = mutation_result + cna_result + sv_result - #Query DB to update alteration_driver_annotation table data, one record at a time + # Query DB to update alteration_driver_annotation table data, one record at a time. update_annotations(all_results, connection, cursor, study_id) - #Commit changes to the database at once to ensure a unique transaction + # Commit changes to the database at once to ensure a unique transaction. try: connection.commit() print('Update complete') diff --git a/core/src/main/scripts/migrate_db.py b/core/src/main/scripts/migrate_db.py index c2f1c0c0a05..6ae0463d249 100755 --- a/core/src/main/scripts/migrate_db.py +++ b/core/src/main/scripts/migrate_db.py @@ -6,20 +6,17 @@ import contextlib import argparse from collections import OrderedDict + +from importer.cbioportal_common import get_database_properties, get_db_cursor + import MySQLdb from pathlib import Path # globals ERROR_FILE = sys.stderr OUTPUT_FILE = sys.stdout -DATABASE_HOST = 'db.host' -DATABASE_NAME = 'db.portal_db_name' -DATABASE_USER = 'db.user' -DATABASE_PW = 'db.password' -DATABASE_USE_SSL = 'db.use_ssl' VERSION_TABLE = 'info' VERSION_FIELD = 'DB_SCHEMA_VERSION' -REQUIRED_PROPERTIES = [DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PW, DATABASE_USE_SSL] ALLOWABLE_GENOME_REFERENCES = ['37', 'hg19', 'GRCh37', '38', 'hg38', 'GRCh38', 'mm10', 'GRCm38'] DEFAULT_GENOME_REFERENCE = 'hg19' MULTI_REFERENCE_GENOME_SUPPORT_MIGRATION_STEP = (2, 11, 0) @@ -27,89 +24,15 @@ SAMPLE_FK_MIGRATION_STEP = (2, 12, 9) FUSIONS_VERBOTEN_STEP = (2, 12, 14) + class PortalProperties(object): """ Properties object class, just has fields for db conn """ - def __init__(self, database_host, database_name, database_user, database_pw, database_use_ssl): - # default port: - self.database_port = 3306 - # if there is a port added to the host name, split and use this one: - if ':' in database_host: - host_and_port = database_host.split(':') - self.database_host = host_and_port[0] - if self.database_host.strip() == 'localhost': - print( - "Invalid host config '" + database_host + "' in properties file. If you want to specify a port on local host use '127.0.0.1' instead of 'localhost'", - file=ERROR_FILE) - sys.exit(1) - self.database_port = int(host_and_port[1]) - else: - self.database_host = database_host - self.database_name = database_name + def __init__(self, database_user, database_pw, database_url): self.database_user = database_user self.database_pw = database_pw - self.database_use_ssl = database_use_ssl + self.database_url = database_url -def get_db_cursor(portal_properties): - """ Establishes a MySQL connection """ - try: - connection_kwargs = {} - connection_kwargs['host'] = portal_properties.database_host - connection_kwargs['port'] = portal_properties.database_port - connection_kwargs['user'] = portal_properties.database_user - connection_kwargs['passwd'] = portal_properties.database_pw - connection_kwargs['db'] = portal_properties.database_name - if portal_properties.database_use_ssl == 'true': - connection_kwargs['ssl'] = {"ssl_mode": True} - - connection = MySQLdb.connect(**connection_kwargs) - except MySQLdb.Error as exception: - print(exception, file=ERROR_FILE) - port_info = '' - if portal_properties.database_host.strip() != 'localhost': - # only add port info if host is != localhost (since with localhost apparently sockets are used and not the given port) TODO - perhaps this applies for all names vs ips? - port_info = " on port " + str(portal_properties.database_port) - message = ( - "--> Error connecting to server " - + portal_properties.database_host - + port_info) - print(message, file=ERROR_FILE) - raise ConnectionError(message) from exception - if connection is not None: - return connection, connection.cursor() - -def get_portal_properties(properties_filename): - """ Returns a properties object """ - properties = {} - with open(properties_filename, 'r') as properties_file: - for line in properties_file: - line = line.strip() - # skip line if its blank or a comment - if len(line) == 0 or line.startswith('#'): - continue - try: - name, value = line.split('=', maxsplit=1) - except ValueError: - print( - 'Skipping invalid entry in property file: %s' % (line), - file=ERROR_FILE) - continue - properties[name] = value.strip() - missing_properties = [] - for required_property in REQUIRED_PROPERTIES: - if required_property not in properties or len(properties[required_property]) == 0: - missing_properties.append(required_property) - if missing_properties: - print( - 'Missing required properties : (%s)' % (', '.join(missing_properties)), - file=ERROR_FILE) - return None - # return an instance of PortalProperties - return PortalProperties(properties[DATABASE_HOST], - properties[DATABASE_NAME], - properties[DATABASE_USER], - properties[DATABASE_PW], - properties[DATABASE_USE_SSL]) def get_db_version(cursor): """ gets the version number of the database """ @@ -135,6 +58,7 @@ def get_db_version(cursor): return None return version + def is_version_larger(version1, version2): """ Checks if version 1 is larger than version 2 """ if version1[0] > version2[0]: @@ -149,17 +73,21 @@ def is_version_larger(version1, version2): return True return False + def is_version_equal(version1, version2): """ Checks if version 1 is equal to version 2""" return version1[0] == version2[0] and version1[1] == version2[1] and version1[2] == version2[2] + def print_all_check_reference_genome_warnings(warnings, force_migration): """ Format warnings for output according to mode, and print to ERROR_FILE """ - space = ' ' + space = ' ' indent = 28 * space allowable_reference_genome_string = ','.join(ALLOWABLE_GENOME_REFERENCES) - clean_up_string = ' Please clean up the mutation_event table and ensure it only contains references to one of the valid reference genomes (%s).' % (allowable_reference_genome_string) - use_default_string = 'the default reference genome (%s) will be used in place of invalid reference genomes and the first encountered reference genome will be used.' % (DEFAULT_GENOME_REFERENCE) + clean_up_string = ' Please clean up the mutation_event table and ensure it only contains references to one of the valid reference genomes (%s).' % ( + allowable_reference_genome_string) + use_default_string = 'the default reference genome (%s) will be used in place of invalid reference genomes and the first encountered reference genome will be used.' % ( + DEFAULT_GENOME_REFERENCE) use_force_string = 'OR use the "--force" option to override this warning, then %s' % (use_default_string) forcing_string = '--force option in effect : %s' % (use_default_string) for warning in warnings: @@ -168,17 +96,21 @@ def print_all_check_reference_genome_warnings(warnings, force_migration): else: print('%s%s%s\n%s%s\n' % (indent, warning, clean_up_string, indent, use_force_string), file=ERROR_FILE) + def validate_reference_genome_values_for_study(warnings, ncbi_to_count, study): """ check if there are unrecognized or varied ncbi_build values for the study, add to warnings if problems are found """ if len(ncbi_to_count) == 1: - for retrieved_ncbi_build in ncbi_to_count: # single iteration + for retrieved_ncbi_build in ncbi_to_count: # single iteration if retrieved_ncbi_build.upper() not in [x.upper() for x in ALLOWABLE_GENOME_REFERENCES]: - msg = 'WARNING: Study %s contains mutation_event records with unsupported NCBI_BUILD value %s.'%(study, retrieved_ncbi_build) + msg = 'WARNING: Study %s contains mutation_event records with unsupported NCBI_BUILD value %s.' % ( + study, retrieved_ncbi_build) warnings.append(msg) elif len(ncbi_to_count) > 1: - msg = 'WARNING: Study %s contains mutation_event records with %s NCBI_BUILD values {ncbi_build:record_count,...} %s.'%(study, len(ncbi_to_count), ncbi_to_count) + msg = 'WARNING: Study %s contains mutation_event records with %s NCBI_BUILD values {ncbi_build:record_count,...} %s.' % ( + study, len(ncbi_to_count), ncbi_to_count) warnings.append(msg) + def check_reference_genome(portal_properties, cursor, force_migration): """ query database for ncbi_build values, aggregate per study, then validate and report problems """ print('Checking database contents for reference genome information', file=OUTPUT_FILE) @@ -194,13 +126,13 @@ def check_reference_genome(portal_properties, cursor, force_migration): group by CANCER_STUDY_IDENTIFIER, NCBI_BUILD """ cursor.execute(sql_statement) - study_to_ncbi_to_count = {} # {cancer_study_identifier : {ncbi_build : record_count}} + study_to_ncbi_to_count = {} # {cancer_study_identifier : {ncbi_build : record_count}} for row in cursor.fetchall(): retrieved_ncbi_build, ref_count, study = row if study in study_to_ncbi_to_count: study_to_ncbi_to_count[study][retrieved_ncbi_build] = ref_count else: - study_to_ncbi_to_count[study] = {retrieved_ncbi_build : ref_count} + study_to_ncbi_to_count[study] = {retrieved_ncbi_build: ref_count} for study in study_to_ncbi_to_count: validate_reference_genome_values_for_study(warnings, study_to_ncbi_to_count[study], study) except MySQLdb.Error as msg: @@ -211,6 +143,7 @@ def check_reference_genome(portal_properties, cursor, force_migration): if not force_migration: sys.exit(1) + def check_and_exit_if_fusions(cursor): try: cursor.execute( @@ -221,7 +154,9 @@ def check_and_exit_if_fusions(cursor): """) fusion_count = cursor.fetchone() if (fusion_count[0] >= 1): - print('Found %i records in the mutation_event table where the mutation_type was "Fusion". The latest database schema does not allow records in the mutation table where mutation_type is set to "Fusion". Studies linked to existing records of this type should be deleted in order to migrate to DB version 2.12.14' % (fusion_count), file=ERROR_FILE) + print( + 'Found %i records in the mutation_event table where the mutation_type was "Fusion". The latest database schema does not allow records in the mutation table where mutation_type is set to "Fusion". Studies linked to existing records of this type should be deleted in order to migrate to DB version 2.12.14' % ( + fusion_count), file=ERROR_FILE) # get the list of studies that need to be cleaned up cursor.execute( """ @@ -241,11 +176,12 @@ def check_and_exit_if_fusions(cursor): for row in rows: print("\t%s" % (row[0]), file=ERROR_FILE) sys.exit(1) - + except MySQLdb.Error as msg: print(msg, file=ERROR_FILE) sys.exit(1) + # TODO: remove this after we update mysql version def check_and_remove_invalid_foreign_keys(cursor): try: @@ -281,6 +217,7 @@ def check_and_remove_invalid_foreign_keys(cursor): print(msg, file=ERROR_FILE) sys.exit(1) + def check_and_remove_type_of_cancer_id_foreign_key(cursor): """The TYPE_OF_CANCER_ID foreign key in the sample table can be either sample_ibfk_1 or sample_ibfk_2. Figure out which one it is and remove it""" try: @@ -296,7 +233,9 @@ def check_and_remove_type_of_cancer_id_foreign_key(cursor): """) rows = cursor.fetchall() if (len(rows) >= 1): - print('sample_ibfk_1 is the foreign key in table sample for type_of_cancer_id column in table type_of_cancer.', file=OUTPUT_FILE) + print( + 'sample_ibfk_1 is the foreign key in table sample for type_of_cancer_id column in table type_of_cancer.', + file=OUTPUT_FILE) cursor.execute( """ ALTER TABLE `sample` DROP FOREIGN KEY sample_ibfk_1; @@ -314,20 +253,24 @@ def check_and_remove_type_of_cancer_id_foreign_key(cursor): """) rows = cursor.fetchall() if (len(rows) >= 1): - print('sample_ibfk_2 is the foreign key in table sample for type_of_cancer_id column in table type_of_cancer.', file=OUTPUT_FILE) + print( + 'sample_ibfk_2 is the foreign key in table sample for type_of_cancer_id column in table type_of_cancer.', + file=OUTPUT_FILE) cursor.execute( """ ALTER TABLE `sample` DROP FOREIGN KEY sample_ibfk_2; """) - print('sample_ibfk_2 foreign key has been deleted.', file=OUTPUT_FILE) + print('sample_ibfk_2 foreign key has been deleted.', file=OUTPUT_FILE) except MySQLdb.Error as msg: print(msg, file=ERROR_FILE) sys.exit(1) + def strip_trailing_comment_from_line(line): - line_parts = re.split("--\s",line) + line_parts = re.split("--\s", line) return line_parts[0] + def run_migration(db_version, sql_filename, connection, cursor, no_transaction, stop_at_version=None): """ Goes through the sql and runs lines based on the version numbers. SQL version should be stated as follows: @@ -377,6 +320,7 @@ def run_migration(db_version, sql_filename, connection, cursor, no_transaction, else: print('Everything up to date, nothing to migrate.', file=OUTPUT_FILE) + def run_statements(statements, connection, cursor, no_transaction): try: if no_transaction: @@ -401,10 +345,12 @@ def run_statements(statements, connection, cursor, no_transaction): sys.exit(1) connection.commit() + def warn_user(): """Warn the user to back up their database before the script runs.""" response = input( - 'WARNING: This script will alter your database! Be sure to back up your data before running.\nContinue running DB migration? (y/n) ' + 'WARNING: This script will alter your database! Be sure to back up your data before running.\n' + 'Continue running DB migration? (y/n) ' ).strip() while response.lower() != 'y' and response.lower() != 'n': response = input( @@ -413,11 +359,13 @@ def warn_user(): if response.lower() == 'n': sys.exit() + def usage(): print( 'migrate_db.py --properties-file [portal properties file] --sql [sql migration file]', file=OUTPUT_FILE) + def main(): """ main function to run mysql migration """ parser = argparse.ArgumentParser(description='cBioPortal DB migration script') @@ -452,20 +400,15 @@ def main(): src_root = script_dir.parent.parent.parent.parent sql_filename = src_root / 'db-scripts/src/main/resources/migration.sql' - # check existence of properties file and sql file - if not os.path.exists(properties_filename): - print('properties file %s cannot be found' % (properties_filename), file=ERROR_FILE) - usage() - sys.exit(2) if not os.path.exists(sql_filename): print('sql file %s cannot be found' % (sql_filename), file=ERROR_FILE) usage() sys.exit(2) # parse properties file - portal_properties = get_portal_properties(properties_filename) + portal_properties = get_database_properties(properties_filename) if portal_properties is None: - print('failure reading properties file (%s)' % (properties_filename), file=ERROR_FILE) + print('failure reading properties file (%s)' % properties_filename, file=ERROR_FILE) sys.exit(1) # warn user @@ -482,16 +425,19 @@ def main(): with contextlib.closing(connection): db_version = get_db_version(cursor) if is_version_larger(MULTI_REFERENCE_GENOME_SUPPORT_MIGRATION_STEP, db_version): - run_migration(db_version, sql_filename, connection, cursor, parser.no_transaction, stop_at_version=MULTI_REFERENCE_GENOME_SUPPORT_MIGRATION_STEP) - #retrieve reference genomes from database + run_migration(db_version, sql_filename, connection, cursor, parser.no_transaction, + stop_at_version=MULTI_REFERENCE_GENOME_SUPPORT_MIGRATION_STEP) + # retrieve reference genomes from database check_reference_genome(portal_properties, cursor, parser.force) db_version = get_db_version(cursor) if is_version_larger(SAMPLE_FK_MIGRATION_STEP, db_version): - run_migration(db_version, sql_filename, connection, cursor, parser.no_transaction, stop_at_version=SAMPLE_FK_MIGRATION_STEP) + run_migration(db_version, sql_filename, connection, cursor, parser.no_transaction, + stop_at_version=SAMPLE_FK_MIGRATION_STEP) check_and_remove_type_of_cancer_id_foreign_key(cursor) db_version = get_db_version(cursor) if is_version_larger(FUSIONS_VERBOTEN_STEP, db_version): - run_migration(db_version, sql_filename, connection, cursor, parser.no_transaction, stop_at_version=FUSIONS_VERBOTEN_STEP) + run_migration(db_version, sql_filename, connection, cursor, parser.no_transaction, + stop_at_version=FUSIONS_VERBOTEN_STEP) check_and_exit_if_fusions(cursor) db_version = get_db_version(cursor) run_migration(db_version, sql_filename, connection, cursor, parser.no_transaction) @@ -501,6 +447,7 @@ def main(): check_and_remove_invalid_foreign_keys(cursor) print('Finished.', file=OUTPUT_FILE) + # do main if __name__ == '__main__': main() diff --git a/docker/web-and-data/docker-entrypoint.sh b/docker/web-and-data/docker-entrypoint.sh index 84951a53358..b474a216d5b 100755 --- a/docker/web-and-data/docker-entrypoint.sh +++ b/docker/web-and-data/docker-entrypoint.sh @@ -3,7 +3,7 @@ set -eo pipefail shopt -s nullglob BAKED_IN_WAR_CONFIG_FILE=/cbioportal-webapp/WEB-INF/classes/portal.properties -CUSTOM_PROPERTIES_FILE=/cbioportal/portal.properties +CUSTOM_PROPERTIES_FILE=cbioportal/portal.properties # check to see if this file is being run or sourced from another script _is_sourced() { @@ -23,19 +23,57 @@ parse_db_params_from_config_and_command_line() { else PROPERTIES_FILE=$BAKED_IN_WAR_CONFIG_FILE fi - - for param in db.host db.user db.portal_db_name db.password; do + for param in db.host db.user db.portal_db_name db.password db.connection_string; do if $(parse_db_params_from_command_line $@ | grep -q $param); then - parse_db_params_from_command_line $@ | grep "^$param" + prop=$(parse_db_params_from_command_line $@ | grep "^$param" || [[ $? == 1 ]]) else - grep -v '^#' $PROPERTIES_FILE | grep "^$param" + prop=$(grep -v '^#' $PROPERTIES_FILE | grep "^$param" || [[ $? == 1 ]]) + fi + if [[ -n "$prop" ]] + then + # Replace dot in parameter name with underscore. + prop=$(sed "s/^db\./db_/" <<< $prop) + if [[ $param == db.connection_string ]] + then + # Remove the parameters (?...) from the connection URL. + echo $(sed -r "s/^([^=]+)=([^\?]+).*/\1=\2/" <<< $prop) + else + echo $prop + fi fi done } +parse_connection_string() { + # Adapted from: https://stackoverflow.com/a/45977232 + readonly URI_REGEX='^(([^:/?#]+):)+?(//((([^:/?#]+)@)?([^:/?#]+)(:([0-9]+))?))?(/([^?#]*))(\?([^#]*))?(#(.*))?' + # ↑↑ ↑ ↑↑↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ + # |2 scheme | ||6 userinfo 7 host | 9 port | 11 rpath | 13 query | 15 fragment + # 1 scheme: | |5 userinfo@ 8 :… 10 path 12 ?… 14 #… + # | 4 authority + # 3 //… + echo db_host=$([[ "$1" =~ $URI_REGEX ]] && echo "${BASH_REMATCH[7]}") + echo db_port=$([[ "$1" =~ $URI_REGEX ]] && echo "${BASH_REMATCH[9]}") +} + check_db_connection() { - eval "$(parse_db_params_from_config_and_command_line $@ | sed 's/^db\./db_/g')" - POTENTIAL_DB_PARAMS=$@ + eval $(parse_db_params_from_config_and_command_line $@) + + if [[ -n $db_host ]] || [[ -n $db_portal_db_name ]] || [[ -n $db_use_ssl ]] + then + echo "----------------------------------------------------------------------------------------------------------------" + echo "-- Connection error:" + echo "-- You try to connect to the database using the deprecated 'db.host', 'db.portal_db_name' and 'db.use_ssl' properties." + echo "-- Please remove these properties and use the 'db.connection_string' property instead. See https://docs.cbioportal.org/deployment/customization/portal.properties-reference/" + echo "-- for assistance on building a valid connection string." + echo "------------------------------------------------------------f---------------------------------------------------" + exit 1 + fi + + if [[ -n $db_connection_string ]] + then + eval "$(parse_connection_string $db_connection_string)" + fi if [ -z ${db_port+x} ] # is $db_port unset? then @@ -46,7 +84,6 @@ check_db_connection() { fi fi - while ! mysqladmin ping -s -h$(echo ${db_host} | cut -d: -f1) -P${db_port} -u${db_user} -p${db_password}; do sleep 5s; diff --git a/docs/Migration-Guide.md b/docs/Migration-Guide.md index 7e5ce3197bb..b5177e87276 100644 --- a/docs/Migration-Guide.md +++ b/docs/Migration-Guide.md @@ -1,22 +1,45 @@ # Migration Guide -This page describes various changes deployers will need to make as they deploy newer versions of the portal. + +This page describes various changes deployers will need to make as they deploy newer versions of the portal. - + +## v5.3.17 + +- Remove `db.host` and `db.portal_db_name` and `db.use_ssl` properties from the _portal.properties_ file or JVM + parameters. Update property `db.connection_string` to encode the hostname, port, database and other parameters + according to [Database Settings](portal.properties-Reference.md#Database-Settings) documentation and pass via + _portal.properties_ file or as JVM parameter. ## v4 -> v5 -- All fusion profiles are now required to be migrated to structural variant format. One can use this [migration tool](https://github.com/cBioPortal/datahub-study-curation-tools/tree/master/fusion-to-sv-converter) to migrate the fusion files. -- All fusion files on [datahub](https://github.com/cBioPortal/datahub) were migrated to the structural variant format and their molecular profile ids were renamed from `{study_id}_fusion` to `{study_id}_structural_variants`. If you are using these datahub files one would need to re-import them. -- Study view user setting will be outdated after migration, please follow `Clear Study View User settings` section in [Session Service Management](Session-Service-Management.md#Clear-Study-View-User-settings) -- The new default set of transcripts for each gene has changed from `uniprot` to `mskcc`. See the [Mutation Data Annotation Section](./mutation-data-transcript-annotation.md) for more details. To keep the old set of default transcripts add the property `genomenexus.isoform_override_source=uniprot` to [the properties file](https://docs.cbioportal.org/deployment/customization/portal.properties-reference/#properties). + +- All fusion profiles are now required to be migrated to structural variant format. One can use + this [migration tool](https://github.com/cBioPortal/datahub-study-curation-tools/tree/master/fusion-to-sv-converter) + to migrate the fusion files. +- All fusion files on [datahub](https://github.com/cBioPortal/datahub) were migrated to the structural variant format + and their molecular profile ids were renamed from `{study_id}_fusion` to `{study_id}_structural_variants`. If you are + using these datahub files one would need to re-import them. +- Study view user setting will be outdated after migration, please follow `Clear Study View User settings` section + in [Session Service Management](Session-Service-Management.md#Clear-Study-View-User-settings) +- The new default set of transcripts for each gene has changed from `uniprot` to `mskcc`. See + the [Mutation Data Annotation Section](./mutation-data-transcript-annotation.md) for more details. To keep the old set + of default transcripts add the property `genomenexus.isoform_override_source=uniprot` + to [the properties file](https://docs.cbioportal.org/deployment/customization/portal.properties-reference/#properties). See the [v5.0.0 release notes](https://github.com/cBioPortal/cbioportal/releases/tag/v5.0.0) for more details. ## v3 -> v4 + - Introduces `logback` package for logging. If you don't have any custom log4j.properties file, no changes are necessary -- Cleans up several old databases ([PR](https://github.com/cBioPortal/cbioportal/pull/9360)). In theory the migration should be seamless, since the docker container detects an old database version and migrates it automatically. +- Cleans up several old databases ([PR](https://github.com/cBioPortal/cbioportal/pull/9360)). In theory the migration + should be seamless, since the docker container detects an old database version and migrates it automatically. See the [v4.0.0 release notes](https://github.com/cBioPortal/cbioportal/releases/tag/v4.0.0) for more details. ## v2 -> v3 -- Session service is now required to be set up. You can't run it without session service. The recommended way to run cBioPortal is to use the Docker Compose instructions. + +- Session service is now required to be set up. You can't run it without session service. The recommended way to run + cBioPortal is to use the Docker Compose instructions. ## v1 -> v2 -- Changes cBioPortal to a Single Page App (SPA) written in React, Mobx and bootstrap that uses a REST API. It shouldn't change anything for a deployer. + +- Changes cBioPortal to a Single Page App (SPA) written in React, Mobx and bootstrap that uses a REST API. It shouldn't + change anything for a deployer. diff --git a/docs/deployment/customization/portal.properties-Reference.md b/docs/deployment/customization/portal.properties-Reference.md index 89cadeee479..44273b6041f 100644 --- a/docs/deployment/customization/portal.properties-Reference.md +++ b/docs/deployment/customization/portal.properties-Reference.md @@ -7,18 +7,25 @@ This page describes the main properties within portal.properties. ``` db.user= db.password= -db.host=[e.g. localhost to connect via socket, or e.g. 127.0.0.1:3307 to connect to a different port like 3307. Used by Java data import layer] -db.portal_db_name=[the database name in mysql, e.g. cbiodb] +db.connection_string= db.driver=[this is the name of your JDBC driver, e.g., com.mysql.jdbc.Driver] ``` -Include `db_connection_string` with the format specified below, and replace `localhost` by the value of `db.host`: +The format of the `db.connection_string` is: +``` +jdbc:mysql://:/?&& +``` + +For example: ``` -db.connection_string=jdbc:mysql://localhost/ +jdbc:mysql://localhost:3306/cbiodb?zeroDateTimeBehavior=convertToNull&useSSL=false ``` -db.tomcat\_resource\_name is required in order to work with the tomcat database connection pool and should have the default value jdbc/cbioportal in order to work correctly with the your WAR file. +:warning: The fields `db.host` and `db.portal_db_name` and `db.use_ssl` are deprecated. It is recommended to configure the database connection using +the `db.connection_string` instead. + +`db.tomcat_resource_name` is required in order to work with the tomcat database connection pool and should have the default value jdbc/cbioportal in order to work correctly with the your WAR file. ``` db.tomcat_resource_name=jdbc/cbioportal diff --git a/pom.xml b/pom.xml index d2c8c98c3a8..2f5c7011afa 100644 --- a/pom.xml +++ b/pom.xml @@ -305,7 +305,7 @@ com.mysql.jdbc.Driver - jdbc:mysql://127.0.0.1:3306/cgds_test?sessionVariables=default_storage_engine=InnoDB&allowLoadLocalInfile=true + jdbc:mysql://127.0.0.1:3306/cgds_test?useSSL=false&sessionVariables=default_storage_engine=InnoDB&allowLoadLocalInfile=true&allowPublicKeyRetrieval=true cbio_user somepassword diff --git a/portal/pom.xml b/portal/pom.xml index 0cf3cf219fe..863d73a87d9 100644 --- a/portal/pom.xml +++ b/portal/pom.xml @@ -215,8 +215,6 @@ ${db.test.username} ${db.test.password} - 127.0.0.1:3306 - cgds_test ${db.test.url} diff --git a/portal/src/integration-tests/saml-oauth2-setup/pom.xml b/portal/src/integration-tests/saml-oauth2-setup/pom.xml index ef5f053e0ee..2db22342264 100644 --- a/portal/src/integration-tests/saml-oauth2-setup/pom.xml +++ b/portal/src/integration-tests/saml-oauth2-setup/pom.xml @@ -66,8 +66,6 @@ ${env.CBIO_TEST_DB_USR} ${env.CBIO_TEST_DB_PSW} - ${env.CBIO_TEST_DB_HOST} - ${env.CBIO_TEST_DB_NAME} ${env.CBIO_TEST_DB_CONNECTION_STRING} file://${project.basedir}/testSamlKeystore.jks diff --git a/src/main/resources/portal.properties.EXAMPLE b/src/main/resources/portal.properties.EXAMPLE index 49aecf9c4d5..7ad52af6203 100644 --- a/src/main/resources/portal.properties.EXAMPLE +++ b/src/main/resources/portal.properties.EXAMPLE @@ -4,10 +4,8 @@ app.name=cbioportal # database db.user=cbio_user db.password=somepassword -db.host=localhost:3306 -db.portal_db_name=cbioportal db.driver=com.mysql.jdbc.Driver -db.connection_string=jdbc:mysql://localhost:3306/ +db.connection_string=jdbc:mysql://localhost:3306/cbioportal?useSSL=false # set tomcat_resource_name when using dbconnector=jndi instead of the default # dbconnector=dbcp. Note that dbconnector needs to be set in CATLINA_OPTS when # using Tomcat (CATALINA_OPTS="-Ddbconnector=jndi"). It does not get picked up @@ -18,7 +16,7 @@ db.connection_string=jdbc:mysql://localhost:3306/ # this should normally be set to false. In some cases you could set this to true (e.g. for testing a feature of a newer release that is not related to the schema change in expected db version above): db.suppress_schema_version_mismatch_errors=false -db.use_ssl=false + # web page cosmetics skin.title=cBioPortal for Cancer Genomics skin.email_contact=cbioportal at googlegroups dot com diff --git a/test/integration/docker-compose-localbuild.yml b/test/integration/docker-compose-localbuild.yml index d7a0523e8a1..cd3d8adc17e 100644 --- a/test/integration/docker-compose-localbuild.yml +++ b/test/integration/docker-compose-localbuild.yml @@ -10,6 +10,8 @@ services: - $PORTAL_INFO_DIR:/portalinfo/ # make docker compose run the cbioportal version-under-test # by volume mounting the local portal source folder into the container - # and running + # and running - $PORTAL_SOURCE_DIR:/cbioportal/ - $PORTAL_SOURCE_DIR/portal/target/cbioportal.war:/app.war + # TODO: remove line below after upgrade of cbioportal-docker-compose to > v5.3.20 + - $PORTAL_SOURCE_DIR/docker/web-and-data/docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh diff --git a/web/src/main/java/org/cbioportal/web/config/WebServletContextListener.java b/web/src/main/java/org/cbioportal/web/config/WebServletContextListener.java index e1d11d3749f..d4a2b8eb4ce 100644 --- a/web/src/main/java/org/cbioportal/web/config/WebServletContextListener.java +++ b/web/src/main/java/org/cbioportal/web/config/WebServletContextListener.java @@ -47,12 +47,12 @@ public void afterPropertiesSet() throws Exception { private void checkOncokbInfo() { if(StringUtils.isEmpty(this.oncokbToken) && (StringUtils.isEmpty(this.oncokbURL) || this.oncokbURL.equalsIgnoreCase(DEFAULT_ONCOKB_URL))) { - System.out.println("----------------------------------------------------------------------------------------------------------------"); + System.out.println("\n----------------------------------------------------------------------------------------------------------------"); // oncokb.org is deprecated, www.oncokb.org should be used System.out.println("-- You are connecting to the OncoKB public instance which does not include any therapeutic information."); System.out.println("-- Please consider obtaining a license to support future OncoKB development by following https://docs.cbioportal.org/2.4-integration-with-other-webservices/oncokb-data-access."); System.out.println("-- Thank you."); - System.out.println("----------------------------------------------------------------------------------------------------------------"); + System.out.println("----------------------------------------------------------------------------------------------------------------\n"); } }