blob: ec60b741d1374c26a82bd6986f35e876245b3dbe [file] [log] [blame]
/*
* 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 {nonEmpty} from 'app/utils/lodashMixins';
import { Bean } from './Beans';
import AbstractTransformer from './AbstractTransformer';
import StringBuilder from './StringBuilder';
import VersionService from 'app/services/Version.service';
const versionService = new VersionService();
const STORE_FACTORY = ['org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStoreFactory'];
// Descriptors for generation of demo data.
const PREDEFINED_QUERIES = [
{
schema: 'CARS',
type: 'PARKING',
create: [
'CREATE TABLE IF NOT EXISTS CARS.PARKING (',
'ID INTEGER NOT NULL PRIMARY KEY,',
'NAME VARCHAR(50) NOT NULL,',
'CAPACITY INTEGER NOT NULL)'
],
clearQuery: ['DELETE FROM CARS.PARKING'],
insertCntConsts: [{name: 'DEMO_MAX_PARKING_CNT', val: 5, comment: 'How many parkings to generate.'}],
insertPattern: ['INSERT INTO CARS.PARKING(ID, NAME, CAPACITY) VALUES(?, ?, ?)'],
fillInsertParameters(sb) {
sb.append('stmt.setInt(1, id);');
sb.append('stmt.setString(2, "Parking #" + (id + 1));');
sb.append('stmt.setInt(3, 10 + rnd.nextInt(20));');
},
selectQuery: ['SELECT * FROM PARKING WHERE CAPACITY >= 20']
},
{
schema: 'CARS',
type: 'CAR',
create: [
'CREATE TABLE IF NOT EXISTS CARS.CAR (',
'ID INTEGER NOT NULL PRIMARY KEY,',
'PARKING_ID INTEGER NOT NULL,',
'NAME VARCHAR(50) NOT NULL);'
],
clearQuery: ['DELETE FROM CARS.CAR'],
rndRequired: true,
insertCntConsts: [
{name: 'DEMO_MAX_CAR_CNT', val: 10, comment: 'How many cars to generate.'},
{name: 'DEMO_MAX_PARKING_CNT', val: 5, comment: 'How many parkings to generate.'}
],
insertPattern: ['INSERT INTO CARS.CAR(ID, PARKING_ID, NAME) VALUES(?, ?, ?)'],
fillInsertParameters(sb) {
sb.append('stmt.setInt(1, id);');
sb.append('stmt.setInt(2, rnd.nextInt(DEMO_MAX_PARKING_CNT));');
sb.append('stmt.setString(3, "Car #" + (id + 1));');
},
selectQuery: ['SELECT * FROM CAR WHERE PARKINGID = 2']
},
{
type: 'COUNTRY',
create: [
'CREATE TABLE IF NOT EXISTS COUNTRY (',
'ID INTEGER NOT NULL PRIMARY KEY,',
'NAME VARCHAR(50),',
'POPULATION INTEGER NOT NULL);'
],
clearQuery: ['DELETE FROM COUNTRY'],
insertCntConsts: [{name: 'DEMO_MAX_COUNTRY_CNT', val: 5, comment: 'How many countries to generate.'}],
insertPattern: ['INSERT INTO COUNTRY(ID, NAME, POPULATION) VALUES(?, ?, ?)'],
fillInsertParameters(sb) {
sb.append('stmt.setInt(1, id);');
sb.append('stmt.setString(2, "Country #" + (id + 1));');
sb.append('stmt.setInt(3, 10000000 + rnd.nextInt(100000000));');
},
selectQuery: ['SELECT * FROM COUNTRY WHERE POPULATION BETWEEN 15000000 AND 25000000']
},
{
type: 'DEPARTMENT',
create: [
'CREATE TABLE IF NOT EXISTS DEPARTMENT (',
'ID INTEGER NOT NULL PRIMARY KEY,',
'COUNTRY_ID INTEGER NOT NULL,',
'NAME VARCHAR(50) NOT NULL);'
],
clearQuery: ['DELETE FROM DEPARTMENT'],
rndRequired: true,
insertCntConsts: [
{name: 'DEMO_MAX_DEPARTMENT_CNT', val: 5, comment: 'How many departments to generate.'},
{name: 'DEMO_MAX_COUNTRY_CNT', val: 5, comment: 'How many countries to generate.'}
],
insertPattern: ['INSERT INTO DEPARTMENT(ID, COUNTRY_ID, NAME) VALUES(?, ?, ?)'],
fillInsertParameters(sb) {
sb.append('stmt.setInt(1, id);');
sb.append('stmt.setInt(2, rnd.nextInt(DEMO_MAX_COUNTRY_CNT));');
sb.append('stmt.setString(3, "Department #" + (id + 1));');
},
selectQuery: ['SELECT * FROM DEPARTMENT']
},
{
type: 'EMPLOYEE',
create: [
'CREATE TABLE IF NOT EXISTS EMPLOYEE (',
'ID INTEGER NOT NULL PRIMARY KEY,',
'DEPARTMENT_ID INTEGER NOT NULL,',
'MANAGER_ID INTEGER,',
'FIRST_NAME VARCHAR(50) NOT NULL,',
'LAST_NAME VARCHAR(50) NOT NULL,',
'EMAIL VARCHAR(50) NOT NULL,',
'PHONE_NUMBER VARCHAR(50),',
'HIRE_DATE DATE NOT NULL,',
'JOB VARCHAR(50) NOT NULL,',
'SALARY DOUBLE);'
],
clearQuery: ['DELETE FROM EMPLOYEE'],
rndRequired: true,
insertCntConsts: [
{name: 'DEMO_MAX_EMPLOYEE_CNT', val: 10, comment: 'How many employees to generate.'},
{name: 'DEMO_MAX_DEPARTMENT_CNT', val: 5, comment: 'How many departments to generate.'}
],
customGeneration(sb, conVar, stmtVar) {
sb.append(`${stmtVar} = ${conVar}.prepareStatement("INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");`);
sb.emptyLine();
sb.startBlock('for (int id = 0; id < DEMO_MAX_EMPLOYEE_CNT; id ++) {');
sb.append('int depId = rnd.nextInt(DEMO_MAX_DEPARTMENT_CNT);');
sb.emptyLine();
sb.append('stmt.setInt(1, DEMO_MAX_DEPARTMENT_CNT + id);');
sb.append('stmt.setInt(2, depId);');
sb.append('stmt.setInt(3, depId);');
sb.append('stmt.setString(4, "First name manager #" + (id + 1));');
sb.append('stmt.setString(5, "Last name manager#" + (id + 1));');
sb.append('stmt.setString(6, "Email manager#" + (id + 1));');
sb.append('stmt.setString(7, "Phone number manager#" + (id + 1));');
sb.append('stmt.setString(8, "2014-01-01");');
sb.append('stmt.setString(9, "Job manager #" + (id + 1));');
sb.append('stmt.setDouble(10, 600.0 + rnd.nextInt(300));');
sb.emptyLine();
sb.append('stmt.executeUpdate();');
sb.endBlock('}');
},
selectQuery: ['SELECT * FROM EMPLOYEE WHERE SALARY > 700']
}
];
// Var name generator function.
const beanNameSeed = () => {
let idx = '';
const names = [];
return (bean) => {
let name;
while (_.includes(names, name = `${bean.id}${idx ? '_' + idx : idx}`))
idx++;
names.push(name);
return name;
};
};
export default class IgniteJavaTransformer extends AbstractTransformer {
// Mapping for objects to method call.
static METHOD_MAPPING = {
'org.apache.ignite.configuration.CacheConfiguration': {
prefix: 'cache',
name: 'name',
args: '',
generator: (sb, id, ccfg) => {
const cacheName = ccfg.findProperty('name').value;
const dataSources = IgniteJavaTransformer.collectDataSources(ccfg);
const javadoc = [
`Create configuration for cache "${cacheName}".`,
'',
'@return Configured cache.'
];
if (dataSources.length)
javadoc.push('@throws Exception if failed to create cache configuration.');
IgniteJavaTransformer.commentBlock(sb, ...javadoc);
sb.startBlock(`public static CacheConfiguration ${id}()${dataSources.length ? ' throws Exception' : ''} {`);
IgniteJavaTransformer.constructBean(sb, ccfg, [], true);
sb.emptyLine();
sb.append(`return ${ccfg.id};`);
sb.endBlock('}');
return sb;
}
},
'org.apache.ignite.cache.store.jdbc.JdbcType': {
prefix: 'jdbcType',
name: 'valueType',
args: 'ccfg.getName()',
generator: (sb, name, jdbcType) => {
const javadoc = [
`Create JDBC type for "${name}".`,
'',
'@param cacheName Cache name.',
'@return Configured JDBC type.'
];
IgniteJavaTransformer.commentBlock(sb, ...javadoc);
sb.startBlock(`private static JdbcType ${name}(String cacheName) {`);
const cacheName = jdbcType.findProperty('cacheName');
cacheName.clsName = 'var';
cacheName.value = 'cacheName';
IgniteJavaTransformer.constructBean(sb, jdbcType);
sb.emptyLine();
sb.append(`return ${jdbcType.id};`);
sb.endBlock('}');
return sb;
}
}
};
// Append comment line.
static comment(sb, ...lines) {
_.forEach(lines, (line) => sb.append(`// ${line}`));
}
// Append comment block.
static commentBlock(sb, ...lines) {
if (lines.length === 1)
sb.append(`/** ${_.head(lines)} **/`);
else {
sb.append('/**');
_.forEach(lines, (line) => sb.append(` * ${line}`));
sb.append(' **/');
}
}
/**
* @param {Bean} bean
*/
static _newBean(bean) {
const shortClsName = this.javaTypes.shortClassName(bean.clsName);
if (_.isEmpty(bean.arguments))
return `new ${shortClsName}()`;
const args = _.map(bean.arguments, (arg) => {
switch (arg.clsName) {
case 'MAP':
return arg.id;
case 'BEAN':
return this._newBean(arg.value);
default:
return this._toObject(arg.clsName, arg.value);
}
});
if (bean.factoryMtd)
return `${shortClsName}.${bean.factoryMtd}(${args.join(', ')})`;
return `new ${shortClsName}(${args.join(', ')})`;
}
/**
* @param {StringBuilder} sb
* @param {String} parentId
* @param {String} propertyName
* @param {String} value
* @private
*/
static _setProperty(sb, parentId, propertyName, value) {
sb.append(`${parentId}.set${_.upperFirst(propertyName)}(${value});`);
}
/**
* @param {StringBuilder} sb
* @param {Array.<String>} vars
* @param {Boolean} limitLines
* @param {Bean} bean
* @param {String} id
* @private
*/
static constructBean(sb, bean, vars = [], limitLines = false, id = bean.id) {
_.forEach(bean.arguments, (arg) => {
switch (arg.clsName) {
case 'MAP':
this._constructMap(sb, arg, vars);
sb.emptyLine();
break;
default:
if (this._isBean(arg.clsName) && arg.value.isComplex()) {
this.constructBean(sb, arg.value, vars, limitLines);
sb.emptyLine();
}
}
});
const clsName = this.javaTypes.shortClassName(bean.clsName);
sb.append(`${this.varInit(clsName, id, vars)} = ${this._newBean(bean)};`);
if (nonEmpty(bean.properties)) {
sb.emptyLine();
this._setProperties(sb, bean, vars, limitLines, id);
}
}
/**
* @param {StringBuilder} sb
* @param {Bean} bean
* @param {Array.<String>} vars
* @param {Boolean} limitLines
* @private
*/
static constructStoreFactory(sb, bean, vars, limitLines = false) {
const shortClsName = this.javaTypes.shortClassName(bean.clsName);
if (_.includes(vars, bean.id))
sb.append(`${bean.id} = ${this._newBean(bean)};`);
else {
vars.push(bean.id);
sb.append(`${shortClsName} ${bean.id} = ${this._newBean(bean)};`);
}
sb.emptyLine();
sb.startBlock(`${bean.id}.setDataSourceFactory(new Factory<DataSource>() {`);
this.commentBlock(sb, '{@inheritDoc}');
sb.startBlock('@Override public DataSource create() {');
sb.append(`return DataSources.INSTANCE_${bean.findProperty('dataSourceBean').id};`);
sb.endBlock('};');
sb.endBlock('});');
const storeFactory = _.cloneDeep(bean);
_.remove(storeFactory.properties, (p) => _.includes(['dataSourceBean'], p.name));
if (storeFactory.properties.length) {
sb.emptyLine();
this._setProperties(sb, storeFactory, vars, limitLines);
}
}
static _isBean(clsName) {
return this.javaTypes.nonBuiltInClass(clsName) && this.javaTypesNonEnum.nonEnum(clsName) && _.includes(clsName, '.');
}
static _toObject(clsName, val) {
const items = _.isArray(val) ? val : [val];
if (clsName === 'EVENTS') {
const lastIdx = items.length - 1;
return [..._.map(items, (v, idx) => (idx === 0 ? 'new int[] {' : ' ') + v.label + (lastIdx === idx ? '}' : ''))];
}
return _.map(items, (item) => {
if (_.isNil(item))
return 'null';
switch (clsName) {
case 'byte':
return `(byte) ${item}`;
case 'float':
return `${item}f`;
case 'double':
return `${item}`;
case 'long':
return `${item}L`;
case 'java.io.Serializable':
case 'java.lang.String':
return `"${item.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
case 'PATH':
case 'PATH_ARRAY':
return `"${item.replace(/\\/g, '\\\\')}"`;
case 'java.lang.Class':
return `${this.javaTypes.shortClassName(item)}.class`;
case 'java.util.UUID':
return `UUID.fromString("${item}")`;
case 'PROPERTY':
return `props.getProperty("${item}")`;
case 'PROPERTY_CHAR':
return `props.getProperty("${item}").toCharArray()`;
case 'PROPERTY_INT':
return `Integer.parseInt(props.getProperty("${item}"))`;
default:
if (this._isBean(clsName) || val instanceof Bean) {
if (item.isComplex())
return item.id;
return this._newBean(item);
}
if (this.javaTypesNonEnum.nonEnum(clsName))
return item;
return `${this.javaTypes.shortClassName(clsName)}.${item}`;
}
});
}
static _mapperId(mapper) {
return (item) => this.javaTypes.toJavaName(mapper.prefix, item.findProperty(mapper.name).value);
}
static _constructBeans(sb, type, items, vars, limitLines) {
if (this._isBean(type)) {
// Construct objects inline for preview or simple objects.
const mapper = this.METHOD_MAPPING[type];
const nextId = mapper ? this._mapperId(mapper) : beanNameSeed();
// Prepare objects refs.
return _.map(items, (item) => {
if (limitLines && mapper)
return nextId(item) + (limitLines ? `(${mapper.args})` : '');
if (item.isComplex()) {
const id = nextId(item);
this.constructBean(sb, item, vars, limitLines, id);
sb.emptyLine();
return id;
}
return this._newBean(item);
});
}
return this._toObject(type, items);
}
/**
*
* @param sb
* @param parentId
* @param arrProp
* @param vars
* @param limitLines
* @private
*/
static _setVarArg(sb, parentId, arrProp, vars, limitLines) {
const refs = this._constructBeans(sb, arrProp.typeClsName, arrProp.items, vars, limitLines);
// Set refs to property.
if (refs.length === 1)
this._setProperty(sb, parentId, arrProp.name, _.head(refs));
else {
sb.startBlock(`${parentId}.set${_.upperFirst(arrProp.name)}(`);
const lastIdx = refs.length - 1;
_.forEach(refs, (ref, idx) => {
sb.append(ref + (lastIdx !== idx ? ',' : ''));
});
sb.endBlock(');');
}
}
/**
*
* @param sb
* @param parentId
* @param arrProp
* @param vars
* @param limitLines
* @private
*/
static _setArray(sb, parentId, arrProp, vars, limitLines) {
const refs = this._constructBeans(sb, arrProp.typeClsName, arrProp.items, vars, limitLines);
const arrType = this.javaTypes.shortClassName(arrProp.typeClsName);
// Set refs to property.
sb.startBlock(`${parentId}.set${_.upperFirst(arrProp.name)}(new ${arrType}[] {`);
const lastIdx = refs.length - 1;
_.forEach(refs, (ref, idx) => sb.append(ref + (lastIdx !== idx ? ',' : '')));
sb.endBlock('});');
}
static _constructMap(sb, map, vars = []) {
const keyClsName = this.javaTypes.shortClassName(map.keyClsName);
const valClsName = this.javaTypes.shortClassName(map.valClsName);
const genericTypeShort = map.keyClsGenericType ? this.javaTypes.shortClassName(map.keyClsGenericType) : '';
const keyClsGeneric = map.keyClsGenericType ?
map.isKeyClsGenericTypeExtended ? `<? extends ${genericTypeShort}>` : `<${genericTypeShort}>`
: '';
const mapClsName = map.ordered ? 'LinkedHashMap' : 'HashMap';
const type = `${mapClsName}<${keyClsName}${keyClsGeneric}, ${valClsName}>`;
sb.append(`${this.varInit(type, map.id, vars)} = new ${mapClsName}<>();`);
sb.emptyLine();
_.forEach(map.entries, (entry) => {
const key = this._toObject(map.keyClsName, entry[map.keyField]);
const val = entry[map.valField];
if (_.isArray(val) && map.valClsName === 'java.lang.String') {
if (val.length > 1) {
sb.startBlock(`${map.id}.put(${key},`);
_.forEach(val, (line, idx) => {
sb.append(`"${line}"${idx !== val.length - 1 ? ' +' : ''}`);
});
sb.endBlock(');');
}
else
sb.append(`${map.id}.put(${key}, ${this._toObject(map.valClsName, _.head(val))});`);
}
else
sb.append(`${map.id}.put(${key}, ${this._toObject(map.valClsNameShow || map.valClsName, val)});`);
});
}
static varInit(type, id, vars) {
if (_.includes(vars, id))
return id;
vars.push(id);
return `${type} ${id}`;
}
/**
*
* @param {StringBuilder} sb
* @param {Bean} bean
* @param {String} id
* @param {Array.<String>} vars
* @param {Boolean} limitLines
* @returns {StringBuilder}
*/
static _setProperties(sb = new StringBuilder(), bean, vars = [], limitLines = false, id = bean.id) {
_.forEach(bean.properties, (prop, idx) => {
switch (prop.clsName) {
case 'DATA_SOURCE':
this._setProperty(sb, id, 'dataSource', `DataSources.INSTANCE_${prop.id}`);
break;
case 'EVENT_TYPES':
if (prop.eventTypes.length === 1) {
const evtGrp = _.head(prop.eventTypes);
this._setProperty(sb, id, prop.name, evtGrp.label);
}
else {
const evtGrp = _.map(prop.eventTypes, 'label');
sb.append(`int[] ${prop.id} = new int[${_.head(evtGrp)}.length`);
_.forEach(_.tail(evtGrp), (evtGrp) => {
sb.append(` + ${evtGrp}.length`);
});
sb.append('];');
sb.emptyLine();
sb.append('int k = 0;');
_.forEach(evtGrp, (evtGrp, evtIdx) => {
sb.emptyLine();
sb.append(`System.arraycopy(${evtGrp}, 0, ${prop.id}, k, ${evtGrp}.length);`);
if (evtIdx < evtGrp.length - 1)
sb.append(`k += ${evtGrp}.length;`);
});
sb.emptyLine();
sb.append(`cfg.setIncludeEventTypes(${prop.id});`);
}
break;
case 'ARRAY':
if (prop.varArg)
this._setVarArg(sb, id, prop, vars, limitLines);
else
this._setArray(sb, id, prop, vars, limitLines);
break;
case 'PATH_ARRAY':
if (prop.varArg)
this._setVarArg(sb, id, prop, this._toObject(prop.clsName, prop.items), limitLines);
else
this._setArray(sb, id, prop, this._toObject(prop.clsName, prop.items), limitLines);
break;
case 'COLLECTION':
const nonBean = !this._isBean(prop.typeClsName);
if (nonBean && prop.implClsName === 'java.util.ArrayList') {
const items = _.map(prop.items, (item) => this._toObject(prop.typeClsName, item));
if (items.length > 1) {
sb.startBlock(`${id}.set${_.upperFirst(prop.name)}(Arrays.asList(`);
_.forEach(items, (item, i) => sb.append(item + (i !== items.length - 1 ? ',' : '')));
sb.endBlock('));');
}
else
this._setProperty(sb, id, prop.name, `Arrays.asList(${items})`);
}
else {
const colTypeClsName = this.javaTypes.shortClassName(prop.typeClsName);
const implClsName = this.javaTypes.shortClassName(prop.implClsName);
sb.append(`${this.varInit(`${implClsName}<${colTypeClsName}>`, prop.id, vars)} = new ${implClsName}<>();`);
sb.emptyLine();
if (nonBean) {
_.forEach(this._toObject(colTypeClsName, prop.items), (item) => {
if (this.javaTypesNonEnum.nonEnum(prop.typeClsName))
sb.append(`${prop.id}.add("${item}");`);
else
sb.append(`${prop.id}.add(${item});`);
sb.emptyLine();
});
}
else {
_.forEach(prop.items, (item) => {
this.constructBean(sb, item, vars, limitLines);
sb.append(`${prop.id}.add(${item.id});`);
sb.emptyLine();
});
}
this._setProperty(sb, id, prop.name, prop.id);
}
break;
case 'MAP':
this._constructMap(sb, prop, vars);
if (nonEmpty(prop.entries))
sb.emptyLine();
this._setProperty(sb, id, prop.name, prop.id);
break;
case 'java.util.Properties':
sb.append(`${this.varInit('Properties', prop.id, vars)} = new Properties();`);
if (nonEmpty(prop.entries))
sb.emptyLine();
_.forEach(prop.entries, (entry) => {
const key = this._toObject('java.lang.String', entry.name);
const val = this._toObject('java.lang.String', entry.value);
sb.append(`${prop.id}.setProperty(${key}, ${val});`);
});
sb.emptyLine();
this._setProperty(sb, id, prop.name, prop.id);
break;
case 'BEAN':
const embedded = prop.value;
if (_.includes(STORE_FACTORY, embedded.clsName)) {
this.constructStoreFactory(sb, embedded, vars, limitLines);
sb.emptyLine();
this._setProperty(sb, id, prop.name, embedded.id);
}
else if (embedded.isComplex()) {
this.constructBean(sb, embedded, vars, limitLines);
sb.emptyLine();
this._setProperty(sb, id, prop.name, embedded.id);
}
else
this._setProperty(sb, id, prop.name, this._newBean(embedded));
break;
default:
this._setProperty(sb, id, prop.name, this._toObject(prop.clsName, prop.value));
}
this._emptyLineIfNeeded(sb, bean.properties, idx);
});
return sb;
}
static _collectMapImports(prop) {
const imports = [];
imports.push(prop.ordered ? 'java.util.LinkedHashMap' : 'java.util.HashMap');
imports.push(prop.keyClsName);
imports.push(prop.valClsName);
if (prop.keyClsGenericType)
imports.push(prop.keyClsGenericType);
return imports;
}
static collectBeanImports(bean) {
const imports = [bean.clsName];
_.forEach(bean.arguments, (arg) => {
switch (arg.clsName) {
case 'BEAN':
imports.push(...this.collectPropertiesImports(arg.value.properties));
break;
case 'java.lang.Class':
imports.push(this.javaTypes.fullClassName(arg.value));
break;
case 'MAP':
imports.push(...this._collectMapImports(arg));
break;
default:
imports.push(arg.clsName);
}
});
imports.push(...this.collectPropertiesImports(bean.properties));
if (_.includes(STORE_FACTORY, bean.clsName))
imports.push('javax.sql.DataSource', 'javax.cache.configuration.Factory');
return imports;
}
/**
* @param {Array.<Object>} props
* @returns {Array.<String>}
*/
static collectPropertiesImports(props) {
const imports = [];
_.forEach(props, (prop) => {
switch (prop.clsName) {
case 'DATA_SOURCE':
imports.push(prop.value.clsName);
break;
case 'PROPERTY':
case 'PROPERTY_CHAR':
case 'PROPERTY_INT':
imports.push('java.io.InputStream', 'java.util.Properties');
break;
case 'BEAN':
imports.push(...this.collectBeanImports(prop.value));
break;
case 'ARRAY':
if (!prop.varArg)
imports.push(prop.typeClsName);
if (this._isBean(prop.typeClsName))
_.forEach(prop.items, (item) => imports.push(...this.collectBeanImports(item)));
if (prop.typeClsName === 'java.lang.Class')
_.forEach(prop.items, (item) => imports.push(item));
break;
case 'COLLECTION':
imports.push(prop.typeClsName);
if (this._isBean(prop.typeClsName)) {
_.forEach(prop.items, (item) => imports.push(...this.collectBeanImports(item)));
imports.push(prop.implClsName);
}
else if (prop.implClsName === 'java.util.ArrayList')
imports.push('java.util.Arrays');
else
imports.push(prop.implClsName);
break;
case 'MAP':
imports.push(...this._collectMapImports(prop));
break;
default:
if (!this.javaTypesNonEnum.nonEnum(prop.clsName))
imports.push(prop.clsName);
}
});
return imports;
}
static _prepareImports(imports) {
return _.sortedUniq(_.sortBy(_.filter(imports, (cls) => !_.startsWith(cls, 'java.lang.') && _.includes(cls, '.'))));
}
/**
* @param {Bean} bean
* @returns {Array.<String>}
*/
static collectStaticImports(bean) {
const imports = [];
_.forEach(bean.properties, (prop) => {
switch (prop.clsName) {
case 'EVENT_TYPES':
_.forEach(prop.eventTypes, (grp) => {
imports.push(`${grp.class}.${grp.value}`);
});
break;
case 'MAP':
if (prop.valClsNameShow === 'EVENTS') {
_.forEach(prop.entries, (lnr) => {
_.forEach(lnr.eventTypes, (type) => imports.push(`${type.class}.${type.label}`));
});
}
break;
default:
// No-op.
}
});
return imports;
}
/**
* @param {Bean} bean
* @returns {Object}
*/
static collectBeansWithMapping(bean) {
const beans = {};
_.forEach(bean.properties, (prop) => {
switch (prop.clsName) {
case 'BEAN':
_.merge(beans, this.collectBeansWithMapping(prop.value));
break;
case 'ARRAY':
if (this._isBean(prop.typeClsName)) {
const mapper = this.METHOD_MAPPING[prop.typeClsName];
const mapperId = mapper ? this._mapperId(mapper) : null;
_.reduce(prop.items, (acc, item) => {
if (mapperId)
acc[mapperId(item)] = item;
_.merge(acc, this.collectBeansWithMapping(item));
return acc;
}, beans);
}
break;
default:
// No-op.
}
});
return beans;
}
/**
* Build Java startup class with configuration.
*
* @param {Bean} cfg
* @param {Object} targetVer Version of Ignite for generated project.
* @param pkg Package name.
* @param {String} clsName Class name for generate factory class otherwise generate code snippet.
* @param {Array.<Object>} clientNearCaches Is client node.
* @returns {StringBuilder}
*/
static igniteConfiguration(cfg, targetVer, pkg, clsName, clientNearCaches) {
const available = versionService.since.bind(versionService, targetVer.ignite);
const sb = new StringBuilder();
sb.append(`package ${pkg};`);
sb.emptyLine();
const imports = this.collectBeanImports(cfg);
const nearCacheBeans = [];
if (nonEmpty(clientNearCaches)) {
imports.push('org.apache.ignite.configuration.NearCacheConfiguration');
_.forEach(clientNearCaches, (cache) => {
const nearCacheBean = this.generator.cacheNearClient(cache, available);
nearCacheBean.cacheName = cache.name;
imports.push(...this.collectBeanImports(nearCacheBean));
nearCacheBeans.push(nearCacheBean);
});
}
if (_.includes(imports, 'oracle.jdbc.pool.OracleDataSource'))
imports.push('java.sql.SQLException');
const hasProps = this.hasProperties(cfg);
if (hasProps)
imports.push('java.util.Properties', 'java.io.InputStream');
_.forEach(this._prepareImports(imports), (cls) => sb.append(`import ${cls};`));
sb.emptyLine();
const staticImports = this._prepareImports(this.collectStaticImports(cfg));
if (staticImports.length) {
_.forEach(this._prepareImports(staticImports), (cls) => sb.append(`import static ${cls};`));
sb.emptyLine();
}
this.mainComment(sb);
sb.startBlock(`public class ${clsName} {`);
// 2. Add external property file
if (hasProps) {
this.commentBlock(sb, 'Secret properties loading.');
sb.append('private static final Properties props = new Properties();');
sb.emptyLine();
sb.startBlock('static {');
sb.startBlock('try (InputStream in = IgniteConfiguration.class.getClassLoader().getResourceAsStream("secret.properties")) {');
sb.append('props.load(in);');
sb.endBlock('}');
sb.startBlock('catch (Exception ignored) {');
sb.append('// No-op.');
sb.endBlock('}');
sb.endBlock('}');
sb.emptyLine();
}
// 3. Add data sources.
const dataSources = this.collectDataSources(cfg);
if (dataSources.length) {
this.commentBlock(sb, 'Helper class for datasource creation.');
sb.startBlock('public static class DataSources {');
_.forEach(dataSources, (ds, idx) => {
const dsClsName = this.javaTypes.shortClassName(ds.clsName);
if (idx !== 0)
sb.emptyLine();
sb.append(`public static final ${dsClsName} INSTANCE_${ds.id} = create${ds.id}();`);
sb.emptyLine();
sb.startBlock(`private static ${dsClsName} create${ds.id}() {`);
if (dsClsName === 'OracleDataSource')
sb.startBlock('try {');
this.constructBean(sb, ds);
sb.emptyLine();
sb.append(`return ${ds.id};`);
if (dsClsName === 'OracleDataSource') {
sb.endBlock('}');
sb.startBlock('catch (SQLException ex) {');
sb.append('throw new Error(ex);');
sb.endBlock('}');
}
sb.endBlock('}');
});
sb.endBlock('}');
sb.emptyLine();
}
_.forEach(nearCacheBeans, (nearCacheBean) => {
this.commentBlock(sb, `Configuration of near cache for cache: ${nearCacheBean.cacheName}.`,
'',
'@return Near cache configuration.',
'@throws Exception If failed to construct near cache configuration instance.'
);
sb.startBlock(`public static NearCacheConfiguration ${nearCacheBean.id}() throws Exception {`);
this.constructBean(sb, nearCacheBean);
sb.emptyLine();
sb.append(`return ${nearCacheBean.id};`);
sb.endBlock('}');
sb.emptyLine();
});
this.commentBlock(sb, 'Configure grid.',
'',
'@return Ignite configuration.',
'@throws Exception If failed to construct Ignite configuration instance.'
);
sb.startBlock('public static IgniteConfiguration createConfiguration() throws Exception {');
this.constructBean(sb, cfg, [], true);
sb.emptyLine();
sb.append(`return ${cfg.id};`);
sb.endBlock('}');
const beans = this.collectBeansWithMapping(cfg);
_.forEach(beans, (bean, id) => {
sb.emptyLine();
this.METHOD_MAPPING[bean.clsName].generator(sb, id, bean);
});
sb.endBlock('}');
return sb;
}
static cluster(cluster, targetVer, pkg, clsName, client) {
const cfg = this.generator.igniteConfiguration(cluster, targetVer, client);
const clientNearCaches = client ? _.filter(cluster.caches, (cache) =>
cache.cacheMode === 'PARTITIONED' && _.get(cache, 'clientNearConfiguration.enabled')) : [];
return this.igniteConfiguration(cfg, targetVer, pkg, clsName, clientNearCaches);
}
/**
* Generate source code for type by its domain model.
*
* @param fullClsName Full class name.
* @param fields Fields.
* @param addConstructor If 'true' then empty and full constructors should be generated.
* @returns {StringBuilder}
*/
static pojo(fullClsName, fields, addConstructor) {
const dotIdx = fullClsName.lastIndexOf('.');
const pkg = fullClsName.substring(0, dotIdx);
const clsName = fullClsName.substring(dotIdx + 1);
const sb = new StringBuilder();
sb.append(`package ${pkg};`);
sb.emptyLine();
const imports = ['java.io.Serializable'];
_.forEach(fields, (field) => imports.push(this.javaTypes.fullClassName(field.javaFieldType)));
_.forEach(this._prepareImports(imports), (cls) => sb.append(`import ${cls};`));
sb.emptyLine();
this.mainComment(sb,
`${clsName} definition.`,
''
);
sb.startBlock(`public class ${clsName} implements Serializable {`);
sb.append('/** */');
sb.append('private static final long serialVersionUID = 0L;');
sb.emptyLine();
// Generate fields declaration.
_.forEach(fields, (field) => {
const fldName = field.javaFieldName;
const fldType = this.javaTypes.shortClassName(field.javaFieldType);
sb.append(`/** Value for ${fldName}. */`);
sb.append(`private ${fldType} ${fldName};`);
sb.emptyLine();
});
// Generate constructors.
if (addConstructor) {
this.commentBlock(sb, 'Empty constructor.');
sb.startBlock(`public ${clsName}() {`);
this.comment(sb, 'No-op.');
sb.endBlock('}');
sb.emptyLine();
this.commentBlock(sb, 'Full constructor.');
const arg = (field) => {
const fldType = this.javaTypes.shortClassName(field.javaFieldType);
return `${fldType} ${field.javaFieldName}`;
};
sb.startBlock(`public ${clsName}(${arg(_.head(fields))}${fields.length === 1 ? ') {' : ','}`);
_.forEach(_.tail(fields), (field, idx) => {
sb.append(`${arg(field)}${idx !== fields.length - 2 ? ',' : ') {'}`);
});
_.forEach(fields, (field) => sb.append(`this.${field.javaFieldName} = ${field.javaFieldName};`));
sb.endBlock('}');
sb.emptyLine();
}
// Generate getters and setters methods.
_.forEach(fields, (field) => {
const fldType = this.javaTypes.shortClassName(field.javaFieldType);
const fldName = field.javaFieldName;
this.commentBlock(sb,
`Gets ${fldName}`,
'',
`@return Value for ${fldName}.`
);
sb.startBlock(`public ${fldType} ${this.javaTypes.toJavaName('get', fldName)}() {`);
sb.append('return ' + fldName + ';');
sb.endBlock('}');
sb.emptyLine();
this.commentBlock(sb,
`Sets ${fldName}`,
'',
`@param ${fldName} New value for ${fldName}.`
);
sb.startBlock(`public void ${this.javaTypes.toJavaName('set', fldName)}(${fldType} ${fldName}) {`);
sb.append(`this.${fldName} = ${fldName};`);
sb.endBlock('}');
sb.emptyLine();
});
// Generate equals() method.
this.commentBlock(sb, '{@inheritDoc}');
sb.startBlock('@Override public boolean equals(Object o) {');
sb.startBlock('if (this == o)');
sb.append('return true;');
sb.endBlock('');
sb.startBlock(`if (!(o instanceof ${clsName}))`);
sb.append('return false;');
sb.endBlock('');
sb.append(`${clsName} that = (${clsName})o;`);
_.forEach(fields, (field) => {
sb.emptyLine();
const javaName = field.javaFieldName;
const javaType = field.javaFieldType;
switch (javaType) {
case 'float':
sb.startBlock(`if (Float.compare(${javaName}, that.${javaName}) != 0)`);
break;
case 'double':
sb.startBlock(`if (Double.compare(${javaName}, that.${javaName}) != 0)`);
break;
default:
if (this.javaTypes.isPrimitive(javaType))
sb.startBlock('if (' + javaName + ' != that.' + javaName + ')');
else
sb.startBlock('if (' + javaName + ' != null ? !' + javaName + '.equals(that.' + javaName + ') : that.' + javaName + ' != null)');
}
sb.append('return false;');
sb.endBlock('');
});
sb.append('return true;');
sb.endBlock('}');
sb.emptyLine();
// Generate hashCode() method.
this.commentBlock(sb, '{@inheritDoc}');
sb.startBlock('@Override public int hashCode() {');
let first = true;
let tempVar = false;
_.forEach(fields, (field) => {
const javaName = field.javaFieldName;
const javaType = field.javaFieldType;
let fldHashCode;
switch (javaType) {
case 'boolean':
fldHashCode = `${javaName} ? 1 : 0`;
break;
case 'byte':
case 'short':
fldHashCode = `(int)${javaName}`;
break;
case 'int':
fldHashCode = `${javaName}`;
break;
case 'long':
fldHashCode = `(int)(${javaName} ^ (${javaName} >>> 32))`;
break;
case 'float':
fldHashCode = `${javaName} != +0.0f ? Float.floatToIntBits(${javaName}) : 0`;
break;
case 'double':
sb.append(`${tempVar ? 'ig_hash_temp' : 'long ig_hash_temp'} = Double.doubleToLongBits(${javaName});`);
tempVar = true;
fldHashCode = '(int) (ig_hash_temp ^ (ig_hash_temp >>> 32))';
break;
default:
fldHashCode = `${javaName} != null ? ${javaName}.hashCode() : 0`;
}
sb.append(first ? `int res = ${fldHashCode};` : `res = 31 * res + ${fldHashCode.startsWith('(') ? fldHashCode : `(${fldHashCode})`};`);
first = false;
sb.emptyLine();
});
sb.append('return res;');
sb.endBlock('}');
sb.emptyLine();
this.commentBlock(sb, '{@inheritDoc}');
sb.startBlock('@Override public String toString() {');
sb.startBlock(`return "${clsName} [" + `);
_.forEach(fields, (field, idx) => {
sb.append(`"${field.javaFieldName}=" + ${field.javaFieldName}${idx < fields.length - 1 ? ' + ", " + ' : ' +'}`);
});
sb.endBlock('"]";');
sb.endBlock('}');
sb.endBlock('}');
return sb.asString();
}
/**
* Generate source code for type by its domain models.
*
* @param caches List of caches to generate POJOs for.
* @param addConstructor If 'true' then generate constructors.
* @param includeKeyFields If 'true' then include key fields into value POJO.
*/
static pojos(caches, addConstructor, includeKeyFields) {
const pojos = [];
_.forEach(caches, (cache) => {
_.forEach(cache.domains, (domain) => {
// Process only domains with 'generatePojo' flag and skip already generated classes.
if (domain.generatePojo && !_.find(pojos, {valueType: domain.valueType}) &&
// Skip domain models without value fields.
nonEmpty(domain.valueFields)) {
const pojo = {
keyType: domain.keyType,
valueType: domain.valueType
};
// Key class generation only if key is not build in java class.
if (this.javaTypes.nonBuiltInClass(domain.keyType) && nonEmpty(domain.keyFields))
pojo.keyClass = this.pojo(domain.keyType, domain.keyFields, addConstructor);
const valueFields = _.clone(domain.valueFields);
if (includeKeyFields) {
_.forEach(domain.keyFields, (fld) => {
if (!_.find(valueFields, {javaFieldName: fld.javaFieldName}))
valueFields.push(fld);
});
}
pojo.valueClass = this.pojo(domain.valueType, valueFields, addConstructor);
pojos.push(pojo);
}
});
});
return pojos;
}
// Generate creation and execution of cache query.
static _multilineQuery(sb, query, prefix, postfix) {
if (_.isEmpty(query))
return;
_.forEach(query, (line, ix) => {
if (ix === 0) {
if (query.length === 1)
sb.append(`${prefix}"${line}"${postfix}`);
else
sb.startBlock(`${prefix}"${line}" +`);
}
else
sb.append(`"${line}"${ix === query.length - 1 ? postfix : ' +'}`);
});
if (query.length > 1)
sb.endBlock('');
else
sb.emptyLine();
}
// Generate creation and execution of prepared statement.
static _prepareStatement(sb, conVar, query) {
this._multilineQuery(sb, query, `${conVar}.prepareStatement(`, ').executeUpdate();');
}
static demoStartup(sb, cluster, shortFactoryCls) {
const cachesWithDataSource = _.filter(cluster.caches, (cache) => {
const kind = _.get(cache, 'cacheStoreFactory.kind');
if (kind) {
const store = cache.cacheStoreFactory[kind];
return (store.connectVia === 'DataSource' || _.isNil(store.connectVia)) && store.dialect;
}
return false;
});
const uniqDomains = [];
// Prepare array of cache and his demo domain model list. Every domain is contained only in first cache.
const demoTypes = _.reduce(cachesWithDataSource, (acc, cache) => {
const domains = _.filter(cache.domains, (domain) => nonEmpty(domain.valueFields) &&
!_.includes(uniqDomains, domain));
if (nonEmpty(domains)) {
uniqDomains.push(...domains);
acc.push({
cache,
domains
});
}
return acc;
}, []);
if (nonEmpty(demoTypes)) {
// Group domain modes by data source
const typeByDs = _.groupBy(demoTypes, ({cache}) => cache.cacheStoreFactory[cache.cacheStoreFactory.kind].dataSourceBean);
let rndNonDefined = true;
const generatedConsts = [];
_.forEach(typeByDs, (types) => {
_.forEach(types, (type) => {
_.forEach(type.domains, (domain) => {
const valType = domain.valueType.toUpperCase();
const desc = _.find(PREDEFINED_QUERIES, (qry) => valType.endsWith(qry.type));
if (desc) {
if (rndNonDefined && desc.rndRequired) {
this.commentBlock(sb, 'Random generator for demo data.');
sb.append('private static final Random rnd = new Random();');
sb.emptyLine();
rndNonDefined = false;
}
_.forEach(desc.insertCntConsts, (cnt) => {
if (!_.includes(generatedConsts, cnt.name)) {
this.commentBlock(sb, cnt.comment);
sb.append(`private static final int ${cnt.name} = ${cnt.val};`);
sb.emptyLine();
generatedConsts.push(cnt.name);
}
});
}
});
});
});
// Generation of fill database method
this.commentBlock(sb, 'Fill data for Demo.');
sb.startBlock('private static void prepareDemoData() throws SQLException {');
let firstDs = true;
_.forEach(typeByDs, (types, ds) => {
const conVar = ds + 'Con';
if (firstDs)
firstDs = false;
else
sb.emptyLine();
sb.startBlock(`try (Connection ${conVar} = ${shortFactoryCls}.DataSources.INSTANCE_${ds}.getConnection()) {`);
let first = true;
let stmtFirst = true;
_.forEach(types, (type) => {
_.forEach(type.domains, (domain) => {
const valType = domain.valueType.toUpperCase();
const desc = _.find(PREDEFINED_QUERIES, (qry) => valType.endsWith(qry.type));
if (desc) {
if (first)
first = false;
else
sb.emptyLine();
this.comment(sb, `Generate ${desc.type}.`);
if (desc.schema)
this._prepareStatement(sb, conVar, [`CREATE SCHEMA IF NOT EXISTS ${desc.schema}`]);
this._prepareStatement(sb, conVar, desc.create);
this._prepareStatement(sb, conVar, desc.clearQuery);
let stmtVar = 'stmt';
if (stmtFirst) {
stmtFirst = false;
stmtVar = 'PreparedStatement stmt';
}
if (_.isFunction(desc.customGeneration))
desc.customGeneration(sb, conVar, stmtVar);
else {
sb.append(`${stmtVar} = ${conVar}.prepareStatement("${desc.insertPattern}");`);
sb.emptyLine();
sb.startBlock(`for (int id = 0; id < ${desc.insertCntConsts[0].name}; id ++) {`);
desc.fillInsertParameters(sb);
sb.emptyLine();
sb.append('stmt.executeUpdate();');
sb.endBlock('}');
}
sb.emptyLine();
sb.append(`${conVar}.commit();`);
}
});
});
sb.endBlock('}');
});
sb.endBlock('}');
sb.emptyLine();
this.commentBlock(sb, 'Print result table to console.');
sb.startBlock('private static void printResult(List<Cache.Entry<Object, Object>> rows) {');
sb.append('for (Cache.Entry<Object, Object> row: rows)');
sb.append(' System.out.println(row);');
sb.endBlock('}');
sb.emptyLine();
// Generation of execute queries method.
this.commentBlock(sb, 'Run demo.');
sb.startBlock('private static void runDemo(Ignite ignite) throws SQLException {');
const getType = (fullType) => fullType.substr(fullType.lastIndexOf('.') + 1);
const cacheLoaded = [];
let rowVariableDeclared = false;
firstDs = true;
_.forEach(typeByDs, (types, ds) => {
const conVar = ds + 'Con';
if (firstDs)
firstDs = false;
else
sb.emptyLine();
sb.startBlock(`try (Connection ${conVar} = ${shortFactoryCls}.DataSources.INSTANCE_${ds}.getConnection()) {`);
let first = true;
_.forEach(types, (type) => {
_.forEach(type.domains, (domain) => {
const valType = domain.valueType.toUpperCase();
const desc = _.find(PREDEFINED_QUERIES, (qry) => valType.endsWith(qry.type));
if (desc) {
if (_.isEmpty(desc.selectQuery))
return;
if (first)
first = false;
else
sb.emptyLine();
const cacheName = type.cache.name;
if (!_.includes(cacheLoaded, cacheName)) {
sb.append(`ignite.cache("${cacheName}").loadCache(null);`);
sb.emptyLine();
cacheLoaded.push(cacheName);
}
const varRows = rowVariableDeclared ? 'rows' : 'List<Cache.Entry<Object, Object>> rows';
this._multilineQuery(sb, desc.selectQuery, `${varRows} = ignite.cache("${cacheName}").query(new SqlQuery<>("${getType(domain.valueType)}", `, ')).getAll();');
sb.append('printResult(rows);');
rowVariableDeclared = true;
}
});
});
sb.endBlock('}');
});
sb.endBlock('}');
}
}
/**
* Function to generate java class for node startup with cluster configuration.
*
* @param {Object} cluster Cluster to process.
* @param {String} fullClsName Full class name.
* @param {String} cfgRef Config.
* @param {String} [factoryCls] fully qualified class name of configuration factory.
* @param {Array.<Object>} [clientNearCaches] Is client node.
*/
static nodeStartup(cluster, fullClsName, cfgRef, factoryCls, clientNearCaches) {
const dotIdx = fullClsName.lastIndexOf('.');
const pkg = fullClsName.substring(0, dotIdx);
const clsName = fullClsName.substring(dotIdx + 1);
const demo = clsName === 'DemoStartup';
const sb = new StringBuilder();
const imports = ['org.apache.ignite.Ignition'];
if (demo) {
imports.push('org.h2.tools.Server', 'java.sql.Connection', 'java.sql.PreparedStatement',
'java.sql.SQLException', 'java.util.Random', 'java.util.List', 'javax.cache.Cache',
'org.apache.ignite.cache.query.SqlQuery');
}
let shortFactoryCls;
if (factoryCls) {
imports.push(factoryCls);
shortFactoryCls = this.javaTypes.shortClassName(factoryCls);
}
if ((nonEmpty(clientNearCaches) || demo) && shortFactoryCls)
imports.push('org.apache.ignite.Ignite');
sb.append(`package ${pkg};`)
.emptyLine();
_.forEach(this._prepareImports(imports), (cls) => sb.append(`import ${cls};`));
sb.emptyLine();
if (demo) {
this.mainComment(sb,
'To start demo configure data sources in secret.properties file.',
'For H2 database it should be like following:',
'dsH2.jdbc.url=jdbc:h2:tcp://localhost/mem:DemoDB;DB_CLOSE_DELAY=-1',
'dsH2.jdbc.username=sa',
'dsH2.jdbc.password=',
''
);
}
else
this.mainComment(sb);
sb.startBlock(`public class ${clsName} {`);
if (demo && shortFactoryCls)
this.demoStartup(sb, cluster, shortFactoryCls);
this.commentBlock(sb,
'Start up node with specified configuration.',
'',
'@param args Command line arguments, none required.',
'@throws Exception If failed.'
);
sb.startBlock('public static void main(String[] args) throws Exception {');
if (demo) {
sb.startBlock('try {');
sb.append('// Start H2 database server.');
sb.append('Server.createTcpServer("-tcpDaemon").start();');
sb.endBlock('}');
sb.startBlock('catch (SQLException ignore) {');
sb.append('// No-op.');
sb.endBlock('}');
sb.emptyLine();
}
if ((nonEmpty(clientNearCaches) || demo) && shortFactoryCls) {
imports.push('org.apache.ignite.Ignite');
sb.append(`Ignite ignite = Ignition.start(${cfgRef});`);
_.forEach(clientNearCaches, (cache, idx) => {
sb.emptyLine();
if (idx === 0)
sb.append('// Demo of near cache creation on client node.');
const nearCacheMtd = this.javaTypes.toJavaName('nearConfiguration', cache.name);
sb.append(`ignite.getOrCreateCache(${shortFactoryCls}.${this.javaTypes.toJavaName('cache', cache.name)}(), ${shortFactoryCls}.${nearCacheMtd}());`);
});
}
else
sb.append(`Ignition.start(${cfgRef});`);
if (demo) {
sb.emptyLine();
sb.append('prepareDemoData();');
sb.emptyLine();
sb.append('runDemo(ignite);');
}
sb.endBlock('}');
sb.endBlock('}');
return sb.asString();
}
/**
* Function to generate java class for load caches.
*
* @param caches Caches to load.
* @param pkg Class package name.
* @param clsName Class name.
* @param {String} cfgRef Config.
*/
static loadCaches(caches, pkg, clsName, cfgRef) {
const sb = new StringBuilder();
sb.append(`package ${pkg};`)
.emptyLine();
const imports = ['org.apache.ignite.Ignition', 'org.apache.ignite.Ignite'];
_.forEach(this._prepareImports(imports), (cls) => sb.append(`import ${cls};`));
sb.emptyLine();
this.mainComment(sb);
sb.startBlock(`public class ${clsName} {`);
this.commentBlock(sb,
'<p>',
'Utility to load caches from database.',
'<p>',
'How to use:',
'<ul>',
' <li>Start cluster.</li>',
' <li>Start this utility and wait while load complete.</li>',
'</ul>',
'',
'@param args Command line arguments, none required.',
'@throws Exception If failed.'
);
sb.startBlock('public static void main(String[] args) throws Exception {');
sb.startBlock(`try (Ignite ignite = Ignition.start(${cfgRef})) {`);
sb.append('System.out.println(">>> Loading caches...");');
sb.emptyLine();
_.forEach(caches, (cache) => {
sb.append('System.out.println(">>> Loading cache: ' + cache.name + '");');
sb.append('ignite.cache("' + cache.name + '").loadCache(null);');
sb.emptyLine();
});
sb.append('System.out.println(">>> All caches loaded!");');
sb.endBlock('}');
sb.endBlock('}');
sb.endBlock('}');
return sb.asString();
}
/**
* Checks if cluster has demo types.
*
* @param cluster Cluster to check.
* @param demo Is demo enabled.
* @returns {boolean} True if cluster has caches with demo types.
*/
static isDemoConfigured(cluster, demo) {
return demo && _.find(cluster.caches, (cache) => _.find(cache.domains, (domain) => _.find(PREDEFINED_QUERIES, (desc) => domain.valueType.toUpperCase().endsWith(desc.type))));
}
}