This is an automated email from the ASF dual-hosted git repository. akuznetsov pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push: new 5aa1481 IGNITE-10214 Web Console: Improved support for JDBC drivers for project generation. 5aa1481 is described below commit 5aa1481aa645b0d42992b49b6e22cd440575e160 Author: Vasiliy Sisko <vsi...@gridgain.com> AuthorDate: Fri Mar 29 14:03:58 2019 +0700 IGNITE-10214 Web Console: Improved support for JDBC drivers for project generation. --- modules/web-console/backend/app/schemas.js | 1 + .../components/modal-import-models/component.js | 14 +++- .../components/cache-edit-form/controller.ts | 10 +++ .../components/cache-edit-form/templates/store.pug | 18 +++-- .../templates/checkpoint/jdbc.pug | 4 +- .../templates/general/discovery/jdbc.pug | 4 +- .../generator/configuration.module.js | 4 +- .../generator/ArtifactVersionChecker.service.js | 86 ++++++++++++++++++++++ .../generator/generator/ConfigurationGenerator.js | 72 +++++++++++++++--- .../generator/generator/Maven.service.js | 38 ++++++---- .../frontend/app/configuration/index.ts | 9 ++- .../frontend/app/configuration/mixins.pug | 3 +- .../frontend/app/configuration/services/Caches.ts | 13 +++- .../app/configuration/services/Clusters.ts | 16 ++-- .../frontend/app/data/pom-dependencies.json | 10 +-- .../app/primitives/form-field/dropdown.pug | 4 +- .../console/agent/handlers/DatabaseListener.java | 28 ++++++- 17 files changed, 274 insertions(+), 60 deletions(-) diff --git a/modules/web-console/backend/app/schemas.js b/modules/web-console/backend/app/schemas.js index 10c294f..29ec9b4 100644 --- a/modules/web-console/backend/app/schemas.js +++ b/modules/web-console/backend/app/schemas.js @@ -226,6 +226,7 @@ module.exports.factory = function(mongoose) { type: String, enum: ['Generic', 'Oracle', 'DB2', 'SQLServer', 'MySQL', 'PostgreSQL', 'H2'] }, + implementationVersion: String, batchSize: Number, maximumPoolSize: Number, maximumWriteAttempts: Number, diff --git a/modules/web-console/frontend/app/configuration/components/modal-import-models/component.js b/modules/web-console/frontend/app/configuration/components/modal-import-models/component.js index b1439e5..9fe23ec 100644 --- a/modules/web-console/frontend/app/configuration/components/modal-import-models/component.js +++ b/modules/web-console/frontend/app/configuration/components/modal-import-models/component.js @@ -375,6 +375,12 @@ export class ModalImportModels { }, { db: 'MySQL', + jdbcDriverClass: 'com.mysql.cj.jdbc.Driver', + jdbcUrl: 'jdbc:mysql://[host]:[port]/[database]', + user: 'root' + }, + { + db: 'MySQL', jdbcDriverClass: 'org.mariadb.jdbc.Driver', jdbcUrl: 'jdbc:mariadb://[host]:[port]/[database]', user: 'root' @@ -452,6 +458,7 @@ export class ModalImportModels { result.jdbcDriverJar = selectedJdbcJar.jdbcDriverJar; result.jdbcDriverClass = selectedJdbcJar.jdbcDriverClass; + result.jdbcDriverImplementationVersion = selectedJdbcJar.jdbcDriverImplementationVersion; return result; } @@ -818,7 +825,8 @@ export class ModalImportModels { kind: 'CacheJdbcPojoStoreFactory', CacheJdbcPojoStoreFactory: { dataSourceBean: 'ds' + dialect + '_' + catalog, - dialect + dialect, + implementationVersion: $scope.selectedPreset.jdbcDriverImplementationVersion }, CacheJdbcBlobStoreFactory: { connectVia: 'DataSource' } }; @@ -1040,7 +1048,8 @@ export class ModalImportModels { label: drv.jdbcDriverJar, value: { jdbcDriverJar: drv.jdbcDriverJar, - jdbcDriverClass: drv.jdbcDriverCls + jdbcDriverClass: drv.jdbcDriverCls, + jdbcDriverImplementationVersion: drv.jdbcDriverImplVersion } }); }); @@ -1093,6 +1102,7 @@ export class ModalImportModels { selectedPreset.db = foundPreset.db; selectedPreset.jdbcDriverJar = foundPreset.jdbcDriverJar; selectedPreset.jdbcDriverClass = foundPreset.jdbcDriverClass; + selectedPreset.jdbcDriverImplementationVersion = foundPreset.jdbcDriverImplementationVersion; selectedPreset.jdbcUrl = foundPreset.jdbcUrl; selectedPreset.user = foundPreset.user; } diff --git a/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cache-edit-form/controller.ts b/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cache-edit-form/controller.ts index 5980613..7facc31 100644 --- a/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cache-edit-form/controller.ts +++ b/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cache-edit-form/controller.ts @@ -85,9 +85,11 @@ export default class CacheEditFormController { {text: 'Save and Download', icon: 'download', click: () => this.save(true)} ]; } + $onDestroy() { this.subscription.unsubscribe(); } + $onChanges(changes) { if ( 'cache' in changes && get(this.clonedCache, '_id') !== get(this.cache, '_id') @@ -105,17 +107,25 @@ export default class CacheEditFormController { this.igfsIDs = (changes.igfss.currentValue || []).map((i) => i._id); } } + getValuesToCompare() { return [this.cache, this.clonedCache].map(this.Caches.normalize); } + save(download) { if (this.$scope.ui.inputForm.$invalid) return this.IgniteFormUtils.triggerValidation(this.$scope.ui.inputForm, this.$scope); this.onSave({$event: {cache: cloneDeep(this.clonedCache), download}}); } + reset = (forReal) => forReal ? this.clonedCache = cloneDeep(this.cache) : void 0; + confirmAndReset() { return this.IgniteConfirm.confirm('Are you sure you want to undo all changes for current cache?') .then(this.reset); } + + clearImplementationVersion(storeFactory) { + delete storeFactory.implementationVersion; + } } diff --git a/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cache-edit-form/templates/store.pug b/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cache-edit-form/templates/store.pug index e9a14e4..96ce5a4 100644 --- a/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cache-edit-form/templates/store.pug +++ b/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cache-edit-form/templates/store.pug @@ -59,7 +59,7 @@ panel-collapsible(ng-form=form on-open=`ui.loadPanel('${form}')`) -var pojoStoreFactory = `${storeFactory}.CacheJdbcPojoStoreFactory` -var required = `${storeFactoryKind} === 'CacheJdbcPojoStoreFactory'` - .pc-form-grid-col-60 + .pc-form-grid-col-30 +form-field__text({ label: 'Data source bean name:', model: `${pojoStoreFactory}.dataSourceBean`, @@ -74,7 +74,7 @@ panel-collapsible(ng-form=form on-open=`ui.loadPanel('${form}')`) +form-field__error({ error: 'required', message: 'Data source bean name is required' }) +form-field__error({ error: 'isValidJavaIdentifier', message: 'Data source bean name is not a valid Java identifier' }) +form-field__error({ error: 'notJavaReservedWord', message: 'Data source bean name should not be a Java reserved word' }) - .pc-form-grid-col-60 + .pc-form-grid-col-30 +form-field__dialect({ label: 'Dialect:', model: `${pojoStoreFactory}.dialect`, @@ -82,8 +82,12 @@ panel-collapsible(ng-form=form on-open=`ui.loadPanel('${form}')`) required, tip: 'Dialect of SQL implemented by a particular RDBMS:', genericDialectName: 'Generic JDBC dialect', - placeholder: 'Choose JDBC dialect' + placeholder: 'Choose JDBC dialect', + change:`$ctrl.clearImplementationVersion(${pojoStoreFactory})` }) + .pc-form-grid-col-60(ng-if=`$ctrl.Caches.requiresProprietaryDrivers(${pojoStoreFactory})`) + a.link-success(ng-href=`{{ $ctrl.Caches.JDBCDriverURL(${pojoStoreFactory}) }}` target='_blank') + | Download JDBC drivers? .pc-form-grid-col-30 +form-field__number({ label:'Batch size:', @@ -193,7 +197,7 @@ panel-collapsible(ng-form=form on-open=`ui.loadPanel('${form}')`) -var required = `${storeFactoryKind} === 'CacheJdbcBlobStoreFactory' && ${blobStoreFactoryVia} !== 'URL'` - .pc-form-grid-col-60(ng-if-start=`${blobStoreFactoryVia} !== 'URL'`) + .pc-form-grid-col-30(ng-if-start=`${blobStoreFactoryVia} !== 'URL'`) +form-field__text({ label: 'Data source bean name:', model: `${blobStoreFactory}.dataSourceBean`, @@ -208,7 +212,7 @@ panel-collapsible(ng-form=form on-open=`ui.loadPanel('${form}')`) +form-field__error({ error: 'required', message: 'Data source bean name is required' }) +form-field__error({ error: 'isValidJavaIdentifier', message: 'Data source bean name is not a valid Java identifier' }) +form-field__error({ error: 'notJavaReservedWord', message: 'Data source bean name should not be a Java reserved word' }) - .pc-form-grid-col-60(ng-if-end) + .pc-form-grid-col-30(ng-if-end) +form-field__dialect({ label: 'Database:', model: `${blobStoreFactory}.dialect`, @@ -218,7 +222,9 @@ panel-collapsible(ng-form=form on-open=`ui.loadPanel('${form}')`) genericDialectName: 'Generic database', placeholder: 'Choose database' }) - + .pc-form-grid-col-60(ng-if=`$ctrl.Caches.requiresProprietaryDrivers(${blobStoreFactory})`) + a.link-success(ng-href=`{{ $ctrl.Caches.JDBCDriverURL(${blobStoreFactory}) }}` target='_blank') + | Download JDBC drivers? .pc-form-grid-col-60 +form-field__checkbox({ label: 'Init schema', diff --git a/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cluster-edit-form/templates/checkpoint/jdbc.pug b/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cluster-edit-form/templates/checkpoint/jdbc.pug index be4afc4..ed5f148 100644 --- a/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cluster-edit-form/templates/checkpoint/jdbc.pug +++ b/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cluster-edit-form/templates/checkpoint/jdbc.pug @@ -37,7 +37,9 @@ include /app/helpers/jade/mixins genericDialectName: 'Generic JDBC dialect', placeholder: 'Choose JDBC dialect' }) - +.pc-form-grid-col-60(ng-if='$ctrl.Clusters.requiresProprietaryDrivers($checkpointSPI.JDBC)') + a.link-success(ng-href='{{ $ctrl.Clusters.JDBCDriverURL($checkpointSPI.JDBC) }}' target='_blank') + | Download JDBC drivers? .pc-form-grid-col-60 +form-field__java-class({ label: 'Listener:', diff --git a/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cluster-edit-form/templates/general/discovery/jdbc.pug b/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cluster-edit-form/templates/general/discovery/jdbc.pug index eb9f0aa..a285408 100644 --- a/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cluster-edit-form/templates/general/discovery/jdbc.pug +++ b/modules/web-console/frontend/app/configuration/components/page-configure-advanced/components/cluster-edit-form/templates/general/discovery/jdbc.pug @@ -47,6 +47,6 @@ mixin discovery-jdbc(modelAt = '$ctrl.clonedCluster') name: '"initSchema"', tip: 'Flag indicating whether DB schema should be initialized by Ignite or was explicitly created by user' }) - .pc-form-grid-col-30(ng-if=`$ctrl.Clusters.requiresProprietaryDrivers(${modelAt})`) - a.link-success(ng-href=`{{ $ctrl.Clusters.JDBCDriverURL(${modelAt}) }}` target='_blank') + .pc-form-grid-col-30(ng-if=`$ctrl.Clusters.requiresProprietaryDrivers(${modelAt}.discovery.Jdbc)`) + a.link-success(ng-href=`{{ $ctrl.Clusters.JDBCDriverURL(${modelAt}.discovery.Jdbc) }}` target='_blank') | Download JDBC drivers? \ No newline at end of file diff --git a/modules/web-console/frontend/app/configuration/generator/configuration.module.js b/modules/web-console/frontend/app/configuration/generator/configuration.module.js index cc04095..fbbfc01 100644 --- a/modules/web-console/frontend/app/configuration/generator/configuration.module.js +++ b/modules/web-console/frontend/app/configuration/generator/configuration.module.js @@ -34,6 +34,7 @@ import IgniteMavenGenerator from './generator/Maven.service'; import IgniteGeneratorProperties from './generator/Properties.service'; import IgniteReadmeGenerator from './generator/Readme.service'; import IgniteCustomGenerator from './generator/Custom.service'; +import IgniteArtifactVersionUtils from './generator/ArtifactVersionChecker.service'; // Ignite events groups. @@ -55,4 +56,5 @@ export default angular .service('IgniteReadmeGenerator', IgniteReadmeGenerator) .service('IgniteDockerGenerator', IgniteDockerGenerator) .service('IgniteMavenGenerator', IgniteMavenGenerator) - .service('IgniteCustomGenerator', IgniteCustomGenerator); + .service('IgniteCustomGenerator', IgniteCustomGenerator) + .service('IgniteArtifactVersionUtils', IgniteArtifactVersionUtils); diff --git a/modules/web-console/frontend/app/configuration/generator/generator/ArtifactVersionChecker.service.js b/modules/web-console/frontend/app/configuration/generator/generator/ArtifactVersionChecker.service.js new file mode 100644 index 0000000..0aa8ff1 --- /dev/null +++ b/modules/web-console/frontend/app/configuration/generator/generator/ArtifactVersionChecker.service.js @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import _ from 'lodash'; + +export default class ArtifactVersionChecker { + /** + * Compare two numbers. + * + * @param a {Number} First number to compare + * @param b {Number} Second number to compare. + * @return {Number} 1 when a is greater then b, -1 when b is greater then a, 0 when a and b is equal. + */ + static _numberComparator(a, b) { + return a > b ? 1 : a < b ? -1 : 0; + } + + /** + * Compare to version. + * + * @param {Object} a first compared version. + * @param {Object} b second compared version. + * @returns {Number} 1 if a > b, 0 if versions equals, -1 if a < b + */ + static _compare(a, b) { + for (let i = 0; i < a.length && i < b.length; i++) { + const res = this._numberComparator(a[i], b[i]); + + if (res !== 0) + return res; + } + + return 0; + } + + /** + * Tries to parse JDBC driver version. + * + * @param {String} ver - String representation of version. + * @returns {Number[]} - Array of version parts. + */ + static _parse(ver) { + return _.map(ver.split(/[.-]/), (v) => { + return v.startsWith('jre') ? parseInt(v.substring(3), 10) : parseInt(v, 10); + }); + } + + /** + * Stay only latest versions of the same dependencies. + * + * @param deps Array of dependencies. + */ + static latestVersions(deps) { + return _.map(_.values(_.groupBy(_.uniqWith(deps, _.isEqual), (dep) => dep.groupId + dep.artifactId)), (arr) => { + if (arr.length > 1) { + try { + return _.reduce(arr, (resDep, dep) => { + if (this._compare(this._parse(dep.version), this._parse(resDep.version)) > 0) + return dep; + + return resDep; + }); + } + catch (err) { + return _.last(_.sortBy(arr, 'version')); + } + } + + return arr[0]; + }); + } +} diff --git a/modules/web-console/frontend/app/configuration/generator/generator/ConfigurationGenerator.js b/modules/web-console/frontend/app/configuration/generator/generator/ConfigurationGenerator.js index 358a453..973db07 100644 --- a/modules/web-console/frontend/app/configuration/generator/generator/ConfigurationGenerator.js +++ b/modules/web-console/frontend/app/configuration/generator/generator/ConfigurationGenerator.js @@ -23,6 +23,7 @@ import IgniteClusterDefaults from './defaults/Cluster.service'; import IgniteEventGroups from './defaults/Event-groups.service'; import IgniteCacheDefaults from './defaults/Cache.service'; import IgniteIGFSDefaults from './defaults/IGFS.service'; +import ArtifactVersionChecker from './ArtifactVersionChecker.service'; import JavaTypes from '../../../services/JavaTypes.service'; import VersionService from 'app/services/Version.service'; @@ -37,6 +38,9 @@ const igfsDflts = new IgniteIGFSDefaults(); const javaTypes = new JavaTypes(clusterDflts, cacheDflts, igfsDflts); const versionService = new VersionService(); +// Pom dependency information. +import POM_DEPENDENCIES from 'app/data/pom-dependencies.json'; + export default class IgniteConfigurationGenerator { static eventGrps = new IgniteEventGroups(); @@ -142,7 +146,7 @@ export default class IgniteConfigurationGenerator { return DFLT_DIALECTS[dialect] || 'Unknown database: ' + (dialect || 'Choose JDBC dialect'); } - static dataSourceBean(id, dialect) { + static dataSourceBean(id, dialect, available, storeDeps, implementationVersion) { let dsBean; switch (dialect) { @@ -170,7 +174,13 @@ export default class IgniteConfigurationGenerator { break; case 'MySQL': - dsBean = new Bean('com.mysql.jdbc.jdbc2.optional.MysqlDataSource', id, {}) + const dep = storeDeps + ? _.find(storeDeps, (d) => d.name === dialect) + : _.first(ArtifactVersionChecker.latestVersions(this._getArtifact({dialect, implementationVersion}, available))); + + const ver = parseInt(dep.version.split('.')[0], 10); + + dsBean = new Bean(ver < 8 ? 'com.mysql.jdbc.jdbc2.optional.MysqlDataSource' : 'com.mysql.cj.jdbc.MysqlDataSource', id, {}) .property('URL', `${id}.jdbc.url`, 'jdbc:mysql://[host]:[port]/[database]'); break; @@ -278,7 +288,7 @@ export default class IgniteConfigurationGenerator { if (ipFinder.includes('dataSourceBean', 'dialect')) { const id = ipFinder.valueOf('dataSourceBean'); - ipFinder.dataSource(id, 'dataSource', this.dataSourceBean(id, ipFinder.valueOf('dialect'))); + ipFinder.dataSource(id, 'dataSource', this.dataSourceBean(id, ipFinder.valueOf('dialect'), available)); } break; @@ -414,8 +424,50 @@ export default class IgniteConfigurationGenerator { }, available); } + /** + * Get dependency artifact for specified datasource. + * + * @param source Datasource. + * @param available Function to check version availability. + * @return {Array<{{name: String, version: String}}>} Array of accordance datasource artifacts. + */ + static _getArtifact(source, available) { + const deps = _.get(POM_DEPENDENCIES, source.dialect); + + if (!deps) + return []; + + const extractVersion = (version) => { + return _.isArray(version) ? _.find(version, (v) => available(v.range)).version : version; + }; + + return _.map(_.castArray(deps), ({version}) => { + return ({ + name: source.dialect, + version: source.implementationVersion || extractVersion(version) + }); + }); + } + static clusterCaches(cluster, caches, igfss, available, client, cfg = this.igniteConfigurationBean(cluster)) { - const ccfgs = _.map(caches, (cache) => this.cacheConfiguration(cache, available)); + const usedDataSourceVersions = []; + + if (cluster.discovery.kind === 'Jdbc') + usedDataSourceVersions.push(...this._getArtifact(cluster.discovery.Jdbc, available)); + + _.forEach(cluster.checkpointSpi, (spi) => { + if (spi.kind === 'JDBC') + usedDataSourceVersions.push(...this._getArtifact(spi.JDBC, available)); + }); + + _.forEach(caches, (cache) => { + if (_.get(cache, 'cacheStoreFactory.kind')) + usedDataSourceVersions.push(...this._getArtifact(cache.cacheStoreFactory[cache.cacheStoreFactory.kind], available)); + }); + + const useDeps = _.uniqWith(ArtifactVersionChecker.latestVersions(usedDataSourceVersions), _.isEqual); + + const ccfgs = _.map(caches, (cache) => this.cacheConfiguration(cache, available, useDeps)); if (!client) { _.forEach(igfss, (igfs) => { @@ -751,7 +803,7 @@ export default class IgniteConfigurationGenerator { const id = jdbcBean.valueOf('dataSourceBean'); const dialect = _.get(spi.JDBC, 'dialect'); - jdbcBean.dataSource(id, 'dataSource', this.dataSourceBean(id, dialect)); + jdbcBean.dataSource(id, 'dataSource', this.dataSourceBean(id, dialect, available)); if (!_.isEmpty(jdbcBean.valueOf('user'))) { jdbcBean.stringProperty('user') @@ -2159,7 +2211,7 @@ export default class IgniteConfigurationGenerator { } // Generate cache store group. - static cacheStore(cache, domains, available, ccfg = this.cacheConfigurationBean(cache)) { + static cacheStore(cache, domains, available, deps, ccfg = this.cacheConfigurationBean(cache)) { const kind = _.get(cache, 'cacheStoreFactory.kind'); if (kind && cache.cacheStoreFactory[kind]) { @@ -2174,7 +2226,7 @@ export default class IgniteConfigurationGenerator { const jdbcId = bean.valueOf('dataSourceBean'); - bean.dataSource(jdbcId, 'dataSourceBean', this.dataSourceBean(jdbcId, storeFactory.dialect)) + bean.dataSource(jdbcId, 'dataSourceBean', this.dataSourceBean(jdbcId, storeFactory.dialect, available, deps, storeFactory.implementationVersion)) .beanProperty('dialect', new EmptyBean(this.dialectClsName(storeFactory.dialect))); bean.intProperty('batchSize') @@ -2219,7 +2271,7 @@ export default class IgniteConfigurationGenerator { if (bean.valueOf('connectVia') === 'DataSource') { const blobId = bean.valueOf('dataSourceBean'); - bean.dataSource(blobId, 'dataSourceBean', this.dataSourceBean(blobId, storeFactory.dialect)); + bean.dataSource(blobId, 'dataSourceBean', this.dataSourceBean(blobId, storeFactory.dialect, available, deps)); } else { ccfg.stringProperty('connectionUrl') @@ -2405,12 +2457,12 @@ export default class IgniteConfigurationGenerator { ccfg.collectionProperty('qryEntities', 'queryEntities', qryEntities, 'org.apache.ignite.cache.QueryEntity'); } - static cacheConfiguration(cache, available, ccfg = this.cacheConfigurationBean(cache)) { + static cacheConfiguration(cache, available, deps = [], ccfg = this.cacheConfigurationBean(cache)) { this.cacheGeneral(cache, available, ccfg); this.cacheAffinity(cache, available, ccfg); this.cacheMemory(cache, available, ccfg); this.cacheQuery(cache, cache.domains, available, ccfg); - this.cacheStore(cache, cache.domains, available, ccfg); + this.cacheStore(cache, cache.domains, available, deps, ccfg); const igfs = _.get(cache, 'nodeFilter.IGFS.instance'); this.cacheNodeFilter(cache, igfs ? [igfs] : [], ccfg); diff --git a/modules/web-console/frontend/app/configuration/generator/generator/Maven.service.js b/modules/web-console/frontend/app/configuration/generator/generator/Maven.service.js index 449bc34..5442b79 100644 --- a/modules/web-console/frontend/app/configuration/generator/generator/Maven.service.js +++ b/modules/web-console/frontend/app/configuration/generator/generator/Maven.service.js @@ -15,14 +15,17 @@ * limitations under the License. */ +import _ from 'lodash'; + import StringBuilder from './StringBuilder'; +import ArtifactVersionChecker from './ArtifactVersionChecker.service'; import VersionService from 'app/services/Version.service'; -const versionService = new VersionService(); - -// Java built-in class names. +// Pom dependency information. import POM_DEPENDENCIES from 'app/data/pom-dependencies.json'; +const versionService = new VersionService(); + /** * Pom file generation entry point. */ @@ -38,22 +41,26 @@ export default class IgniteMavenGenerator { sb.append(`<${tag}>${val}</${tag}>`); } - addDependency(deps, groupId, artifactId, version, jar) { - deps.push({groupId, artifactId, version, jar}); + addComment(sb, comment) { + sb.append(`<!-- ${comment} -->`); } - pickDependency(acc, key, dfltVer, igniteVer) { + addDependency(deps, groupId, artifactId, version, jar, link) { + deps.push({groupId, artifactId, version, jar, link}); + } + + _extractVersion(igniteVer, version) { + return _.isArray(version) ? _.find(version, (v) => versionService.since(igniteVer, v.range)).version : version; + } + + pickDependency(acc, key, dfltVer, igniteVer, storedVer) { const deps = POM_DEPENDENCIES[key]; if (_.isNil(deps)) return; - const extractVersion = (version) => { - return _.isArray(version) ? _.find(version, (v) => versionService.since(igniteVer, v.range)).version : version; - }; - - _.forEach(_.castArray(deps), ({groupId, artifactId, version, jar}) => { - this.addDependency(acc, groupId || 'org.apache.ignite', artifactId, extractVersion(version) || dfltVer, jar); + _.forEach(_.castArray(deps), ({groupId, artifactId, version, jar, link}) => { + this.addDependency(acc, groupId || 'org.apache.ignite', artifactId, storedVer || this._extractVersion(igniteVer, version) || dfltVer, jar, link); }); } @@ -92,6 +99,9 @@ export default class IgniteMavenGenerator { this.addProperty(sb, 'systemPath', '${project.basedir}/jdbc-drivers/' + dep.jar); } + if (dep.link) + this.addComment(sb, `You may download JDBC driver from: ${dep.link}`); + sb.endBlock('</dependency>'); }); @@ -152,7 +162,7 @@ export default class IgniteMavenGenerator { */ storeFactoryDependency(deps, storeFactory, igniteVer) { if (storeFactory.dialect && (!storeFactory.connectVia || storeFactory.connectVia === 'DataSource')) - this.pickDependency(deps, storeFactory.dialect, null, igniteVer); + this.pickDependency(deps, storeFactory.dialect, null, igniteVer, storeFactory.implementationVersion); } collectDependencies(cluster, targetVer) { @@ -211,7 +221,7 @@ export default class IgniteMavenGenerator { if (cluster.logger && cluster.logger.kind) this.pickDependency(deps, cluster.logger.kind, igniteVer); - return _.uniqWith(deps.concat(...storeDeps), _.isEqual); + return _.uniqWith(deps.concat(ArtifactVersionChecker.latestVersions(storeDeps)), _.isEqual); } /** diff --git a/modules/web-console/frontend/app/configuration/index.ts b/modules/web-console/frontend/app/configuration/index.ts index 51ba059..c1fac18 100644 --- a/modules/web-console/frontend/app/configuration/index.ts +++ b/modules/web-console/frontend/app/configuration/index.ts @@ -85,7 +85,13 @@ import { import {errorState} from './transitionHooks/errorState'; import {default as ActivitiesData} from '../core/activities/Activities.data'; +const JDBC_LINKS = { + Oracle: 'https://www.oracle.com/technetwork/database/application-development/jdbc/downloads/index.html', + DB2: 'http://www-01.ibm.com/support/docview.wss?uid=swg21363866' +}; + registerActivitiesHook.$inject = ['$uiRouter', 'IgniteActivitiesData']; + function registerActivitiesHook($uiRouter: UIRouter, ActivitiesData: ActivitiesData) { $uiRouter.transitionService.onSuccess({to: 'base.configuration.**'}, (transition) => { ActivitiesData.post({group: 'configuration', action: transition.targetState().name()}); @@ -183,4 +189,5 @@ export default angular .directive('pcIsInCollection', isInCollection) .directive('fakeUiCanExit', fakeUiCanExit) .directive('formUiCanExitGuard', formUICanExitGuard) - .directive('igniteUiAceTabs', uiAceTabs); + .directive('igniteUiAceTabs', uiAceTabs) + .constant('JDBC_LINKS', JDBC_LINKS); diff --git a/modules/web-console/frontend/app/configuration/mixins.pug b/modules/web-console/frontend/app/configuration/mixins.pug index 92b4d39..f769c69 100644 --- a/modules/web-console/frontend/app/configuration/mixins.pug +++ b/modules/web-console/frontend/app/configuration/mixins.pug @@ -389,13 +389,14 @@ mixin list-pair-edit({ items, keyLbl, valLbl, itemName, itemsName }) label-multiple=itemsName ) -mixin form-field__dialect({ label, model, name, required, tip, genericDialectName, placeholder }) +mixin form-field__dialect({ label, model, name, required, tip, genericDialectName, placeholder, change }) +form-field__dropdown({ label, model, name, required, placeholder, + change, options: '[\ {value: "Generic", label: "' + genericDialectName + '"},\ {value: "Oracle", label: "Oracle"},\ diff --git a/modules/web-console/frontend/app/configuration/services/Caches.ts b/modules/web-console/frontend/app/configuration/services/Caches.ts index a32281f..4a15c93 100644 --- a/modules/web-console/frontend/app/configuration/services/Caches.ts +++ b/modules/web-console/frontend/app/configuration/services/Caches.ts @@ -15,13 +15,14 @@ * limitations under the License. */ +import get from 'lodash/get'; import ObjectID from 'bson-objectid'; import omit from 'lodash/fp/omit'; import {CacheModes, AtomicityModes, ShortCache} from '../types'; import {Menu} from 'app/types'; export default class Caches { - static $inject = ['$http']; + static $inject = ['$http', 'JDBC_LINKS']; cacheModes: Menu<CacheModes> = [ {value: 'LOCAL', label: 'LOCAL'}, @@ -35,7 +36,7 @@ export default class Caches { {value: 'TRANSACTIONAL_SNAPSHOT', label: 'TRANSACTIONAL_SNAPSHOT'} ]; - constructor(private $http: ng.IHttpService) {} + constructor(private $http: ng.IHttpService, private JDBC_LINKS) {} saveCache(cache) { return this.$http.post('/api/v1/configuration/caches/save', cache); @@ -223,4 +224,12 @@ export default class Caches { shouldShowCacheBackupsCount(cache: ShortCache) { return cache && cache.cacheMode === 'PARTITIONED'; } + + requiresProprietaryDrivers(storeFactory) { + return ['Oracle', 'DB2', 'SQLServer'].includes(get(storeFactory, 'dialect')); + } + + JDBCDriverURL(storeFactory) { + return this.JDBC_LINKS[get(storeFactory, 'dialect')]; + } } diff --git a/modules/web-console/frontend/app/configuration/services/Clusters.ts b/modules/web-console/frontend/app/configuration/services/Clusters.ts index 63ad5a8..b03a9e0 100644 --- a/modules/web-console/frontend/app/configuration/services/Clusters.ts +++ b/modules/web-console/frontend/app/configuration/services/Clusters.ts @@ -28,7 +28,7 @@ const uniqueNameValidator = (defaultName = '') => (a, items = []) => { }; export default class Clusters { - static $inject = ['$http']; + static $inject = ['$http', 'JDBC_LINKS']; discoveries: Menu<DiscoveryKinds> = [ {value: 'Vm', label: 'Static IPs'}, @@ -76,7 +76,7 @@ export default class Clusters { /** * Cluster-related configuration stuff */ - constructor(private $http: ng.IHttpService) {} + constructor(private $http: ng.IHttpService, private JDBC_LINKS) {} getConfiguration(clusterID: string) { return this.$http.get(`/api/v1/configuration/${clusterID}`); @@ -224,16 +224,12 @@ export default class Clusters { }; } - requiresProprietaryDrivers(cluster) { - return get(cluster, 'discovery.kind') === 'Jdbc' && ['Oracle', 'DB2', 'SQLServer'].includes(get(cluster, 'discovery.Jdbc.dialect')); + requiresProprietaryDrivers(dataSrc) { + return ['Oracle', 'DB2', 'SQLServer'].includes(get(dataSrc, 'dialect')); } - JDBCDriverURL(cluster) { - return ({ - Oracle: 'http://www.oracle.com/technetwork/database/features/jdbc/default-2280470.html', - DB2: 'http://www-01.ibm.com/support/docview.wss?uid=swg21363866', - SQLServer: 'https://www.microsoft.com/en-us/download/details.aspx?id=11774' - })[get(cluster, 'discovery.Jdbc.dialect')]; + JDBCDriverURL(dataSrc) { + return this.JDBC_LINKS[get(dataSrc, 'dialect')]; } dataRegion = { diff --git a/modules/web-console/frontend/app/data/pom-dependencies.json b/modules/web-console/frontend/app/data/pom-dependencies.json index d13bf14..428455f 100644 --- a/modules/web-console/frontend/app/data/pom-dependencies.json +++ b/modules/web-console/frontend/app/data/pom-dependencies.json @@ -15,14 +15,14 @@ {"groupId": "com.mchange", "artifactId": "c3p0", "version": "0.9.5.2"}, {"groupId": "com.mchange", "artifactId": "mchange-commons-java", "version": "0.2.11"} ], - "MySQL": {"groupId": "mysql", "artifactId": "mysql-connector-java", "version": "5.1.40"}, - "PostgreSQL": {"groupId": "org.postgresql", "artifactId": "postgresql", "version": "9.4.1212.jre7"}, + "MySQL": {"groupId": "mysql", "artifactId": "mysql-connector-java", "version": "8.0.15"}, + "PostgreSQL": {"groupId": "org.postgresql", "artifactId": "postgresql", "version": "42.2.5"}, "H2": {"groupId": "com.h2database", "artifactId": "h2", "version": [ {"range": ["1.0.0", "2.0.0"], "version": "1.4.191"}, {"range": ["2.0.0", "2.7.0"], "version": "1.4.195"}, {"range": "2.7.0", "version": "1.4.197"} ]}, - "Oracle": {"groupId": "com.oracle.jdbc", "artifactId": "ojdbc7", "version": "12.1.0.2", "jar": "ojdbc7.jar"}, - "DB2": {"groupId": "ibm", "artifactId": "jdbc", "version": "4.21.29", "jar": "db2jcc4.jar"}, - "SQLServer": {"groupId": "microsoft", "artifactId": "jdbc", "version": "4.2", "jar": "sqljdbc41.jar"} + "Oracle": {"groupId": "com.oracle.jdbc", "artifactId": "ojdbc8", "version": "18.3.0.0.0", "jar": "ojdbc8.jar", "link": "https://www.oracle.com/technetwork/database/application-development/jdbc/downloads/index.html"}, + "DB2": {"groupId": "ibm", "artifactId": "jdbc", "version": "4.25.13", "jar": "db2jcc4.jar", "link": "http://www-01.ibm.com/support/docview.wss?uid=swg21363866"}, + "SQLServer": {"groupId": "com.microsoft.sqlserver", "artifactId": "mssql-jdbc", "version": "7.2.1.jre8"} } diff --git a/modules/web-console/frontend/app/primitives/form-field/dropdown.pug b/modules/web-console/frontend/app/primitives/form-field/dropdown.pug index b13a177..73cced5 100644 --- a/modules/web-console/frontend/app/primitives/form-field/dropdown.pug +++ b/modules/web-console/frontend/app/primitives/form-field/dropdown.pug @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. -mixin form-field__dropdown({ label, model, name, disabled, required, multiple, placeholder, placeholderEmpty, options, optionLabel = 'label', tip, tipOpts }) +mixin form-field__dropdown({ label, model, name, disabled, required, multiple, placeholder, placeholderEmpty, options, optionLabel = 'label', tip, tipOpts, change }) -var errLbl = label ? label.substring(0, label.length - 1) : 'Field'; mixin __form-field__input() @@ -31,6 +31,8 @@ mixin form-field__dropdown({ label, model, name, disabled, required, multiple, p ng-ref='$input' ng-ref-read='ngModel' + ng-change=change && `${change}` + bs-select bs-options=`item.value as item.${optionLabel} for item in ${options}` diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseListener.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseListener.java index b6bd623..fde38bf 100644 --- a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseListener.java +++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseListener.java @@ -32,6 +32,8 @@ import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.jar.JarFile; +import java.util.jar.Manifest; import org.apache.ignite.console.agent.AgentConfiguration; import org.apache.ignite.console.agent.db.DbMetadataReader; import org.apache.ignite.console.agent.db.DbSchema; @@ -50,6 +52,12 @@ public class DatabaseListener { private static final Logger log = Logger.getLogger(DatabaseListener.class.getName()); /** */ + private static final String IMPLEMENTATION_VERSION = "Implementation-Version"; + + /** */ + private static final String BUNDLE_VERSION = "Bundle-Version"; + + /** */ private final File driversFolder; /** */ @@ -156,17 +164,26 @@ public class DatabaseListener { URL url = new URL("jar", null, "file:" + (win ? "/" : "") + file.getPath() + "!/META-INF/services/java.sql.Driver"); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8))) { + try ( + BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), UTF_8)); + JarFile jar = new JarFile(file.getPath()) + ) { + Manifest m = jar.getManifest(); + Object ver = m.getMainAttributes().getValue(IMPLEMENTATION_VERSION); + + if (ver == null) + ver = m.getMainAttributes().getValue(BUNDLE_VERSION); + String jdbcDriverCls = reader.readLine(); - res.add(new JdbcDriver(file.getName(), jdbcDriverCls)); + res.add(new JdbcDriver(file.getName(), jdbcDriverCls, ver != null ? ver.toString() : null)); if (log.isDebugEnabled()) log.debug("Found: [driver=" + file + ", class=" + jdbcDriverCls + "]"); } } catch (IOException e) { - res.add(new JdbcDriver(file.getName(), null)); + res.add(new JdbcDriver(file.getName(), null, null)); log.info("Found: [driver=" + file + "]"); log.info("Failed to detect driver class: " + e.getMessage()); @@ -319,14 +336,17 @@ public class DatabaseListener { public final String jdbcDriverJar; /** */ public final String jdbcDriverCls; + /** */ + public final String jdbcDriverImplVersion; /** * @param jdbcDriverJar File name of driver jar file. * @param jdbcDriverCls Optional JDBC driver class. */ - public JdbcDriver(String jdbcDriverJar, String jdbcDriverCls) { + public JdbcDriver(String jdbcDriverJar, String jdbcDriverCls, String jdbcDriverImplVersion) { this.jdbcDriverJar = jdbcDriverJar; this.jdbcDriverCls = jdbcDriverCls; + this.jdbcDriverImplVersion = jdbcDriverImplVersion; } } }