You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2015/08/25 09:26:13 UTC

[2/3] ignite git commit: IGNITE-843 WIP on preview.

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b72d82a/modules/control-center-web/src/main/js/routes/generator/generator-java.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/routes/generator/generator-java.js b/modules/control-center-web/src/main/js/routes/generator/generator-java.js
new file mode 100644
index 0000000..9065a2b
--- /dev/null
+++ b/modules/control-center-web/src/main/js/routes/generator/generator-java.js
@@ -0,0 +1,886 @@
+/*
+ * 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.
+ */
+
+if (typeof window === 'undefined') {
+    _ = require('lodash');
+
+    $commonUtils = require('../../helpers/common-utils');
+    $dataStructures = require('../../helpers/data-structures');
+    $generatorCommon = require('./generator-common');
+}
+
+
+/**
+ * Convert some name to valid java name.
+ *
+ * @param prefix To append to java name.
+ * @param name to convert.
+ * @returns {string} Valid java name.
+ */
+function toJavaName(prefix, name) {
+    var javaName = name.replace(/[^A-Za-z_0-9]+/, '_');
+
+    return prefix + javaName.charAt(0).toLocaleUpperCase() + javaName.slice(1);
+}
+
+/**
+ * Add eviction policy.
+ *
+ * @param res Resulting output with generated code.
+ * @param varName Current using variable name.
+ * @param evictionPolicy Data to add.
+ * @param propertyName Name in source data.
+ */
+function addEvictionPolicy(res, varName, evictionPolicy, propertyName) {
+    if (evictionPolicy && evictionPolicy.kind) {
+        var e = $generatorCommon.EVICTION_POLICIES[evictionPolicy.kind];
+
+        var obj = evictionPolicy[evictionPolicy.kind.toUpperCase()];
+
+        addBeanWithProperties(res, varName, obj, propertyName, propertyName, e.className, e.fields, true);
+    }
+}
+
+function addCacheTypeMetadataDatabaseFields(res, meta, fieldProperty) {
+    var dbFields = meta[fieldProperty];
+
+    if (dbFields && dbFields.length > 0) {
+        res.line();
+
+        declareVariable(res, needNewVariable(res, fieldProperty), fieldProperty, 'java.util.Collection', 'java.util.ArrayList', 'org.apache.ignite.cache.CacheTypeFieldMetadata');
+
+        _.forEach(dbFields, function (field) {
+            res.line(fieldProperty + '.add(new CacheTypeFieldMetadata(' +
+                '"' + field.databaseName + '", ' +
+                'java.sql.Types.' + field.databaseType + ', ' +
+                '"' + field.javaName + '", ' +
+                field.javaType + '.class'
+                + '));');
+        });
+
+        res.line('typeMeta.' + toJavaName('set', fieldProperty) + '(' + fieldProperty + ');');
+    }
+}
+
+function addCacheTypeMetadataQueryFields(res, meta, fieldProperty) {
+    var fields = meta[fieldProperty];
+
+    if (fields && fields.length > 0) {
+        res.line();
+
+        declareVariable(res, needNewVariable(res, fieldProperty), fieldProperty, 'java.util.Map', 'java.util.LinkedHashMap', 'java.lang.String', 'java.lang.Class<?>');
+
+        _.forEach(fields, function (field) {
+            res.line(fieldProperty + '.put("' + field.name + '", ' + res.importClass(field.className) + '.class);');
+        });
+
+        res.line('typeMeta.' + toJavaName('set', fieldProperty) + '(' + fieldProperty + ');');
+    }
+}
+
+function addCacheTypeMetadataGroups(res, meta) {
+    var groups = meta.groups;
+
+    if (groups && groups.length > 0) {
+        _.forEach(groups, function (group) {
+            var fields = group.fields;
+
+            if (fields && fields.length > 0) {
+                res.importClass('java.util.Map');
+                res.importClass('java.util.LinkedHashMap');
+                res.importClass('org.apache.ignite.lang.IgniteBiTuple');
+
+                var varNew = !res.groups;
+
+                res.line();
+                res.line((varNew ? 'Map<String, LinkedHashMap<String, IgniteBiTuple<Class<?>, Boolean>>> ' : '') +
+                    "groups = new LinkedHashMap<>();");
+
+                if (varNew)
+                    res.groups = true;
+
+                varNew = !res.groupItems;
+
+                res.line((varNew ? 'LinkedHashMap<String, IgniteBiTuple<Class<?>, Boolean>> ' : '') +
+                    'groupItems = new LinkedHashMap<>();');
+
+                if (varNew)
+                    res.groupItems = true;
+
+                _.forEach(fields, function (field) {
+                    res.line('groupItems.put("' + field.name + '", ' +
+                        'new IgniteBiTuple<Class<?>, Boolean>(' + res.importClass(field.className) + '.class, ' + field.direction + '));');
+                });
+
+                res.line('groups.put("' + group.name + '", groupItems);');
+            }
+        });
+
+        res.line('typeMeta.setGroups(groups);');
+    }
+}
+
+function addCacheTypeMetadataConfiguration(res, meta) {
+    declareVariable(res, needNewVariable(res, 'typeMeta'), 'typeMeta', 'org.apache.ignite.cache.CacheTypeMetadata');
+
+    var kind = meta.kind;
+
+    var keyType = addClassProperty(res, 'typeMeta', meta, 'keyType');
+    addClassProperty(res, 'typeMeta', meta, 'valueType');
+
+    if (kind != 'query') {
+        addProperty(res, 'typeMeta', meta, 'databaseSchema');
+        addProperty(res, 'typeMeta', meta, 'databaseTable');
+
+        if (!$dataStructures.isJavaBuildInClass(keyType))
+            addCacheTypeMetadataDatabaseFields(res, meta, 'keyFields');
+
+        addCacheTypeMetadataDatabaseFields(res, meta, 'valueFields');
+    }
+
+    if (kind != 'store') {
+        addCacheTypeMetadataQueryFields(res, meta, 'queryFields');
+        addCacheTypeMetadataQueryFields(res, meta, 'ascendingFields');
+        addCacheTypeMetadataQueryFields(res, meta, 'descendingFields');
+
+        res.needEmptyLine = true;
+        addListProperty(res, 'typeMeta', meta, 'textFields');
+
+        addCacheTypeMetadataGroups(res, meta);
+    }
+
+    res.line();
+    res.line('types.add(typeMeta);');
+    res.line();
+}
+
+/**
+ * Generate java code for cache configuration.
+ *
+ * @param cache Cache config.
+ * @param varName Variable name.
+ * @param res Result builder.
+ * @returns {*} Append generated java code to builder and return it.
+ */
+function addCacheConfiguration(res, cache, varName) {
+    res.emptyLineIfNeeded();
+
+    res.importClass('org.apache.ignite.cache.CacheAtomicityMode');
+    res.importClass('org.apache.ignite.cache.CacheMode');
+
+    declareVariable(res, true, varName, 'org.apache.ignite.configuration.CacheConfiguration');
+
+    res.needEmptyLine = true;
+
+    addProperty(res, varName, cache, 'name');
+
+    var cacheMode = addProperty(res, varName, cache, 'mode', 'CacheMode', 'cacheMode');
+
+    addProperty(res, varName, cache, 'atomicityMode', 'CacheAtomicityMode');
+
+    if (cacheMode == 'PARTITIONED')
+        addProperty(res, varName, cache, 'backups');
+
+    addProperty(res, varName, cache, 'readFromBackup');
+    addProperty(res, varName, cache, 'startSize');
+
+    res.needEmptyLine = true;
+
+    addProperty(res, varName, cache, 'memoryMode', 'CacheMemoryMode');
+    addProperty(res, varName, cache, 'offHeapMaxMemory');
+    addProperty(res, varName, cache, 'swapEnabled');
+    addProperty(res, varName, cache, 'copyOnRead');
+
+    res.needEmptyLine = true;
+
+    addEvictionPolicy(res, varName, cache.evictionPolicy, 'evictionPolicy');
+
+    if (cacheMode == 'PARTITIONED' && cache.nearCacheEnabled) {
+        res.needEmptyLine = true;
+
+        res.importClass('org.apache.ignite.configuration.NearCacheConfiguration');
+
+        addBeanWithProperties(res, varName, cache.nearConfiguration, 'nearConfiguration', 'nearConfiguration',
+            'NearCacheConfiguration', {nearStartSize: null}, true);
+
+        if (cache.nearConfiguration && cache.nearConfiguration.nearEvictionPolicy && cache.nearConfiguration.nearEvictionPolicy.kind) {
+            addEvictionPolicy(res, 'nearConfiguration', cache.nearConfiguration.nearEvictionPolicy, 'nearEvictionPolicy');
+        }
+    }
+
+    res.needEmptyLine = true;
+
+    addProperty(res, varName, cache, 'sqlEscapeAll');
+    addProperty(res, varName, cache, 'sqlOnheapRowCacheSize');
+    addProperty(res, varName, cache, 'longQueryWarningTimeout');
+
+    if (cache.indexedTypes && cache.indexedTypes.length > 0) {
+        res.emptyLineIfNeeded();
+
+        res.append(varName + '.setIndexedTypes(');
+
+        for (var i = 0; i < cache.indexedTypes.length; i++) {
+            if (i > 0)
+                res.append(', ');
+
+            var pair = cache.indexedTypes[i];
+
+            res.append(toJavaCode(res.importClass(pair.keyClass), 'class')).append(', ').append(toJavaCode(res.importClass(pair.valueClass), 'class'))
+        }
+
+        res.line(');');
+    }
+
+    addMultiparamProperty(res, varName, cache, 'sqlFunctionClasses', 'class');
+
+    res.needEmptyLine = true;
+
+    if (cacheMode != 'LOCAL') {
+        addProperty(res, varName, cache, 'rebalanceMode', 'CacheRebalanceMode');
+        addProperty(res, varName, cache, 'rebalanceThreadPoolSize');
+        addProperty(res, varName, cache, 'rebalanceBatchSize');
+        addProperty(res, varName, cache, 'rebalanceOrder');
+        addProperty(res, varName, cache, 'rebalanceDelay');
+        addProperty(res, varName, cache, 'rebalanceTimeout');
+        addProperty(res, varName, cache, 'rebalanceThrottle');
+
+        res.needEmptyLine = true;
+    }
+
+    if (cache.cacheStoreFactory && cache.cacheStoreFactory.kind) {
+        var storeFactory = cache.cacheStoreFactory[cache.cacheStoreFactory.kind];
+        var data = $generatorCommon.STORE_FACTORIES[cache.cacheStoreFactory.kind];
+
+        var sfVarName = toJavaName('storeFactory', cache.name);
+        var dsVarName = 'none';
+
+        if (storeFactory.dialect) {
+            var dataSourceBean = storeFactory.dataSourceBean;
+
+            dsVarName = toJavaName('dataSource', dataSourceBean);
+
+            if (!_.contains(res.datasourceBeans, dataSourceBean)) {
+                res.datasourceBeans.push(dataSourceBean);
+
+                var dataSource = $generatorCommon.DATA_SOURCES[storeFactory.dialect];
+
+                res.line();
+
+                declareVariable(res, true, dsVarName, dataSource);
+
+                res.line(dsVarName + '.setURL(_URL_);');
+                res.line(dsVarName + '.setUsername(_User_Name_);');
+                res.line(dsVarName + '.setPassword(_Password_);');
+            }
+        }
+
+        addBeanWithProperties(res, varName, storeFactory, 'cacheStoreFactory', sfVarName, data.className,
+            data.fields, true);
+
+        if (dsVarName != 'none')
+            res.line(sfVarName + '.setDataSource(' + dsVarName + ');');
+    }
+
+    res.needEmptyLine = true;
+
+    addProperty(res, varName, cache, 'loadPreviousValue');
+    addProperty(res, varName, cache, 'readThrough');
+    addProperty(res, varName, cache, 'writeThrough');
+
+    res.needEmptyLine = true;
+
+    addProperty(res, varName, cache, 'invalidate');
+    addProperty(res, varName, cache, 'defaultLockTimeout');
+    addProperty(res, varName, cache, 'transactionManagerLookupClassName');
+
+    res.needEmptyLine = true;
+
+    addProperty(res, varName, cache, 'writeBehindEnabled');
+    addProperty(res, varName, cache, 'writeBehindBatchSize');
+    addProperty(res, varName, cache, 'writeBehindFlushSize');
+    addProperty(res, varName, cache, 'writeBehindFlushFrequency');
+    addProperty(res, varName, cache, 'writeBehindFlushThreadCount');
+
+    res.needEmptyLine = true;
+
+    addProperty(res, varName, cache, 'statisticsEnabled');
+    addProperty(res, varName, cache, 'managementEnabled');
+
+    res.needEmptyLine = true;
+
+    addProperty(res, varName, cache, 'maxConcurrentAsyncOperations');
+
+    res.needEmptyLine = true;
+
+    // Generate cache type metadata configs.
+    if ((cache.queryMetadata && cache.queryMetadata.length > 0) ||
+        (cache.storeMetadata && cache.storeMetadata.length > 0)) {
+        res.emptyLineIfNeeded();
+
+        declareVariable(res, needNewVariable(res, 'types'), 'types', 'java.util.Collection', 'java.util.ArrayList', 'org.apache.ignite.cache.CacheTypeMetadata');
+        res.line();
+
+        var metaNames = [];
+
+        if (cache.queryMetadata && cache.queryMetadata.length > 0) {
+            _.forEach(cache.queryMetadata, function (meta) {
+                if (!_.contains(metaNames, meta.name)) {
+                    metaNames.push(meta.name);
+
+                    addCacheTypeMetadataConfiguration(res, meta);
+                }
+            });
+        }
+
+        if (cache.storeMetadata && cache.storeMetadata.length > 0) {
+            _.forEach(cache.storeMetadata, function (meta) {
+                if (!_.contains(metaNames, meta.name)) {
+                    metaNames.push(meta.name);
+
+                    addCacheTypeMetadataConfiguration(res, meta);
+                }
+            });
+        }
+
+        res.line(varName + '.setTypeMetadata(types);');
+    }
+}
+
+function toJavaCode(val, type) {
+    if (val == null)
+        return 'null';
+
+    if (type == 'float')
+        return val + 'f';
+
+    if (type == 'class')
+        return val + '.class';
+
+    if (type)
+        return type + '.' + val;
+
+    if (typeof(val) == 'string')
+        return '"' + val.replace('"', '\\"') + '"';
+
+    if (typeof(val) == 'number' || typeof(val) == 'boolean')
+        return '' + val;
+
+    throw "Unknown type: " + typeof(val) + ' (' + val + ')';
+}
+
+function needNewVariable(res, varName) {
+    var needNew = !res[varName];
+
+    if (needNew)
+        res[varName] = true;
+
+    return needNew;
+}
+
+/**
+ * Add variable declaration.
+ *
+ * @param res Resulting output with generated code.
+ * @param varNew If 'true' then declare new variable otherwise reuse previously declared variable.
+ * @param varName Variable name.
+ * @param varFullType Variable full class name to be added to imports.
+ * @param varFullActualType Variable actual full class name to be added to imports.
+ * @param varFullGenericType1 Optional full class name of first generic.
+ * @param varFullGenericType2 Optional full class name of second generic.
+ */
+function declareVariable(res, varNew, varName, varFullType, varFullActualType, varFullGenericType1, varFullGenericType2) {
+    var varType = res.importClass(varFullType);
+
+    if (varFullActualType && varFullGenericType1) {
+        var varActualType = res.importClass(varFullActualType);
+        var varGenericType1 = res.importClass(varFullGenericType1);
+
+        if (varFullGenericType2)
+            var varGenericType2 = res.importClass(varFullGenericType2);
+
+        res.line((varNew ? (varType + '<' + varGenericType1 + (varGenericType2 ? ', ' + varGenericType2 : '') + '> ') : '') + varName + ' = new ' + varActualType + '<>();');
+    }
+    else
+        res.line((varNew ? (varType + ' ') : '') + varName + ' = new ' + varType + '();');
+}
+
+/**
+ * Add property via setter / property name.
+ *
+ * @param res Resulting output with generated code.
+ * @param varName Variable name.
+ * @param obj Source object with data.
+ * @param propName Property name to take from source object.
+ * @param enumType Optional info about property datatype.
+ * @param setterName Optional special setter name.
+ */
+function addProperty(res, varName, obj, propName, enumType, setterName) {
+    var val = obj[propName];
+
+    if ($commonUtils.isDefined(val)) {
+        res.emptyLineIfNeeded();
+
+        res.line(varName + '.' + getSetterName(setterName ? setterName : propName)
+            + '(' + toJavaCode(val, enumType) + ');');
+    }
+
+    return val;
+}
+
+/**
+ * Add property via setter assuming that it is a 'Class'.
+ *
+ * @param res Resulting output with generated code.
+ * @param varName Variable name.
+ * @param obj Source object with data.
+ * @param propName Property name to take from source object.
+ */
+function addClassProperty(res, varName, obj, propName) {
+    var val = obj[propName];
+
+    if ($commonUtils.isDefined(val)) {
+        res.emptyLineIfNeeded();
+
+        res.line(varName + '.' + getSetterName(propName) + '(' + res.importClass(val) + '.class);');
+    }
+}
+
+/**
+ * @param propName Property name
+ * @returns Property setter with name by java conventions.
+ */
+function getSetterName(propName) {
+    return toJavaName('set', propName);
+}
+
+function addListProperty(res, varName, obj, propName, enumType, setterName) {
+    var val = obj[propName];
+
+    if (val && val.length > 0) {
+        res.emptyLineIfNeeded();
+
+        res.importClass('java.util.Arrays');
+
+        res.append(varName + '.' + getSetterName(setterName ? setterName : propName) + '(Arrays.asList(');
+
+        for (var i = 0; i < val.length; i++) {
+            if (i > 0)
+                res.append(', ');
+
+            res.append(toJavaCode(val[i], enumType));
+        }
+
+        res.line('));');
+    }
+}
+
+function addMultiparamProperty(res, varName, obj, propName, type, setterName) {
+    var val = obj[propName];
+
+    if (val && val.length > 0) {
+        res.append(varName + '.' + getSetterName(setterName ? setterName : propName) + '(');
+
+        for (var i = 0; i < val.length; i++) {
+            if (i > 0)
+                res.append(', ');
+
+            res.append(toJavaCode(val[i], type));
+        }
+
+        res.line(');');
+    }
+}
+
+function addBeanWithProperties(res, varName, bean, beanPropName, beanVarName, beanClass, props, createBeanAlthoughNoProps) {
+    if (bean && $commonUtils.hasProperty(bean, props)) {
+        if (!res.emptyLineIfNeeded()) {
+            res.line();
+        }
+
+        res.line(beanClass + ' ' + beanVarName + ' = new ' + beanClass + '();');
+
+        for (var propName in props) {
+            if (props.hasOwnProperty(propName)) {
+                var descr = props[propName];
+
+                if (descr) {
+                    switch (descr.type) {
+                        case 'list':
+                            addListProperty(res, beanVarName, bean, propName, descr.elementsType, descr.setterName);
+                            break;
+
+                        case 'enum':
+                            addProperty(res, beanVarName, bean, propName, descr.enumClass, descr.setterName);
+                            break;
+
+                        case 'float':
+                            addProperty(res, beanVarName, bean, propName, 'float', descr.setterName);
+                            break;
+
+                        case 'propertiesAsList':
+                            var val = bean[propName];
+
+                            if (val && val.length > 0) {
+                                res.line('Properties ' + descr.propVarName + ' = new Properties();');
+
+                                for (var i = 0; i < val.length; i++) {
+                                    var nameAndValue = val[i];
+
+                                    var eqIndex = nameAndValue.indexOf('=');
+                                    if (eqIndex >= 0) {
+                                        res.line(descr.propVarName + '.setProperty('
+                                            + nameAndValue.substring(0, eqIndex) + ', '
+                                            + nameAndValue.substr(eqIndex + 1) + ');');
+                                    }
+
+                                }
+
+                                res.line(beanVarName + '.' + getSetterName(propName) + '(' + descr.propVarName + ');');
+                            }
+                            break;
+
+                        case 'className':
+                            if (bean[propName]) {
+                                res.line(beanVarName + '.' + getSetterName(propName) + '(new ' + $generatorCommon.KNOWN_CLASSES[bean[propName]].className + '());');
+                            }
+
+                            break;
+
+                        default:
+                            addProperty(res, beanVarName, bean, propName, null, descr.setterName);
+                    }
+                }
+                else {
+                    addProperty(res, beanVarName, bean, propName);
+                }
+            }
+        }
+
+        res.line(varName + '.' + getSetterName(beanPropName) + '(' + beanVarName + ');');
+
+        res.needEmptyLine = true;
+    }
+    else if (createBeanAlthoughNoProps) {
+        res.emptyLineIfNeeded();
+
+        res.line(varName + '.' + getSetterName(beanPropName) + '(new ' + beanClass + '());');
+    }
+}
+
+
+$generatorJava = {};
+
+/**
+ * Function to generate java code for cluster configuration.
+ *
+ * @param cluster Cluster to process.
+ * @param javaClass If 'true' then generate factory class otherwise generate code snippet.
+ * @param clientNearConfiguration Near cache configuration for client node.
+ */
+$generatorJava.generateClusterConfiguration = function (cluster, javaClass, clientNearConfiguration) {
+    var res = $generatorCommon.builder();
+
+    res.datasourceBeans = [];
+
+    if (javaClass) {
+        res.line('/**');
+        res.line(' * ' + $generatorCommon.mainComment());
+        res.line(' */');
+        res.startBlock('public class ConfigurationFactory {');
+        res.line('/**');
+        res.line(' * Configure grid.');
+        res.line(' */');
+        res.startBlock('public IgniteConfiguration createConfiguration() {');
+    }
+
+    declareVariable(res, true, 'cfg', 'org.apache.ignite.configuration.IgniteConfiguration');
+    res.line();
+
+    if (clientNearConfiguration) {
+        res.line('cfg.setClientMode(true);');
+        res.line();
+    }
+
+    if (cluster.discovery) {
+        var d = cluster.discovery;
+
+        declareVariable(res, true, 'discovery', 'org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi');
+
+        switch (d.kind) {
+            case 'Multicast':
+                res.importClass('org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder');
+
+                addBeanWithProperties(res, 'discovery', d.Multicast, 'ipFinder', 'ipFinder',
+                    'TcpDiscoveryMulticastIpFinder', {
+                        multicastGroup: null,
+                        multicastPort: null,
+                        responseWaitTime: null,
+                        addressRequestAttempts: null,
+                        localAddress: null
+                    }, true);
+
+                break;
+
+            case 'Vm':
+                res.importClass('org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder');
+
+                addBeanWithProperties(res, 'discovery', d.Vm, 'ipFinder', 'ipFinder', 'TcpDiscoveryVmIpFinder', {
+                    addresses: {type: 'list'}
+                }, true);
+
+                break;
+
+            case 'S3':
+                res.importClass('org.apache.ignite.spi.discovery.tcp.ipfinder.s3.TcpDiscoveryS3IpFinder');
+
+                if (d.S3)
+                    addBeanWithProperties(res, 'discovery', d.S3, 'ipFinder', 'ipFinder', 'TcpDiscoveryS3IpFinder',
+                        {bucketName: null}, true);
+                else
+                    res.line('discovery.setIpFinder(new TcpDiscoveryS3IpFinder());');
+
+                break;
+
+            case 'Cloud':
+                res.importClass('org.apache.ignite.spi.discovery.tcp.ipfinder.cloud.TcpDiscoveryCloudIpFinder');
+
+                addBeanWithProperties(res, 'discovery', d.Cloud, 'ipFinder', 'ipFinder', 'TcpDiscoveryCloudIpFinder', {
+                    credential: null,
+                    credentialPath: null,
+                    identity: null,
+                    provider: null,
+                    regions: {type: 'list'},
+                    zones: {type: 'list'}
+                }, true);
+
+                break;
+
+            case 'GoogleStorage':
+                res.importClass('org.apache.ignite.spi.discovery.tcp.ipfinder.gce.TcpDiscoveryGoogleStorageIpFinder');
+
+                addBeanWithProperties(res, 'discovery', d.GoogleStorage, 'ipFinder', 'ipFinder',
+                    'TcpDiscoveryGoogleStorageIpFinder', {
+                        projectName: null,
+                        bucketName: null,
+                        serviceAccountP12FilePath: null,
+                        serviceAccountId: null
+                    }, true);
+
+                //if (d.GoogleStorage.addrReqAttempts) todo ????
+                //    res.line('<property name="serviceAccountP12FilePath" value="' + escapeAttr(d.GoogleStorage.addrReqAttempts) + '"/>');
+
+                break;
+
+            case 'Jdbc':
+                res.line();
+
+                declareVariable(res, true, 'ipFinder', 'org.apache.ignite.spi.discovery.tcp.ipfinder.jdbc.TcpDiscoveryJdbcIpFinder');
+
+                res.line('ipFinder.setInitSchema(' + ($commonUtils.isDefined(d.Jdbc.initSchema) && d.Jdbc.initSchema) + ');');
+                res.line('discovery.setIpFinder(ipFinder);');
+                res.needEmptyLine = true;
+
+                break;
+
+            case 'SharedFs':
+                res.importClass('org.apache.ignite.spi.discovery.tcp.ipfinder.sharedfs.TcpDiscoverySharedFsIpFinder');
+
+                addBeanWithProperties(res, 'discovery', d.SharedFs, 'ipFinder', 'ipFinder',
+                    'TcpDiscoverySharedFsIpFinder', {path: null}, true);
+
+                break;
+
+            default:
+                throw 'Unknown discovery kind: ' + d.kind;
+        }
+
+        res.emptyLineIfNeeded();
+
+        res.line('cfg.setDiscoverySpi(discovery);');
+
+        res.needEmptyLine = true;
+    }
+
+    var caches = cluster.caches;
+
+    if (caches && caches.length > 0) {
+        res.emptyLineIfNeeded();
+
+        var names = [];
+
+        _.forEach(caches, function (cache) {
+            res.emptyLineIfNeeded();
+
+            var cacheName = toJavaName('cache', cache.name);
+
+            names.push(cacheName);
+
+            addCacheConfiguration(res, cache, cacheName);
+
+            res.needEmptyLine = true;
+        });
+
+        res.emptyLineIfNeeded();
+
+        res.append('cfg.setCacheConfiguration(');
+
+        for (var i = 0; i < names.length; i++) {
+            if (i > 0)
+                res.append(', ');
+
+            res.append(names[i]);
+        }
+
+        res.line(');');
+
+        res.needEmptyLine = true;
+    }
+
+    var atomicCfg = $generatorCommon.ATOMIC_CONFIGURATION;
+
+    addBeanWithProperties(res, 'cfg', cluster.atomicConfiguration, 'atomicConfiguration', 'atomicCfg',
+        atomicCfg.className, atomicCfg.fields);
+
+    res.needEmptyLine = true;
+
+    addProperty(res, 'cfg', cluster, 'networkTimeout');
+    addProperty(res, 'cfg', cluster, 'networkSendRetryDelay');
+    addProperty(res, 'cfg', cluster, 'networkSendRetryCount');
+    addProperty(res, 'cfg', cluster, 'segmentCheckFrequency');
+    addProperty(res, 'cfg', cluster, 'waitForSegmentOnStart');
+    addProperty(res, 'cfg', cluster, 'discoveryStartupDelay');
+
+    res.needEmptyLine = true;
+
+    addProperty(res, 'cfg', cluster, 'deploymentMode', 'DeploymentMode');
+
+    res.needEmptyLine = true;
+
+    if (cluster.includeEventTypes && cluster.includeEventTypes.length > 0) {
+        res.emptyLineIfNeeded();
+
+        if (cluster.includeEventTypes.length == 1) {
+            res.importClass('org.apache.ignite.events.EventType');
+
+            res.line('cfg.setIncludeEventTypes(EventType.' + cluster.includeEventTypes[0] + ');');
+        }
+        else {
+            res.append('int[] events = new int[EventType.' + cluster.includeEventTypes[0] + '.length');
+
+            for (i = 1; i < cluster.includeEventTypes.length; i++) {
+                res.line();
+
+                res.append('    + EventType.' + cluster.includeEventTypes[i] + '.length');
+            }
+
+            res.line('];');
+            res.line();
+            res.line('int k = 0;');
+
+            for (i = 0; i < cluster.includeEventTypes.length; i++) {
+                res.line();
+
+                var e = cluster.includeEventTypes[i];
+
+                res.line('System.arraycopy(EventType.' + e + ', 0, events, k, EventType.' + e + '.length);');
+                res.line('k += EventType.' + e + '.length;');
+            }
+
+            res.line();
+            res.line('cfg.setIncludeEventTypes(events);');
+        }
+
+        res.needEmptyLine = true;
+    }
+
+    res.needEmptyLine = true;
+
+    var marshaller = cluster.marshaller;
+
+    if (marshaller && marshaller.kind) {
+        var marshallerDesc = $generatorCommon.MARSHALLERS[marshaller.kind];
+
+        addBeanWithProperties(res, 'cfg', marshaller[marshaller.kind], 'marshaller', 'marshaller',
+            marshallerDesc.className, marshallerDesc.fields, true);
+
+        addBeanWithProperties(res, 'marshaller', marshaller[marshaller.kind], marshallerDesc.className, marshallerDesc.fields, true);
+    }
+
+    addProperty(res, 'cfg', cluster, 'marshalLocalJobs');
+    addProperty(res, 'cfg', cluster, 'marshallerCacheKeepAliveTime');
+    addProperty(res, 'cfg', cluster, 'marshallerCacheThreadPoolSize');
+
+    res.needEmptyLine = true;
+
+    addProperty(res, 'cfg', cluster, 'metricsExpireTime');
+    addProperty(res, 'cfg', cluster, 'metricsHistorySize');
+    addProperty(res, 'cfg', cluster, 'metricsLogFrequency');
+    addProperty(res, 'cfg', cluster, 'metricsUpdateFrequency');
+    res.needEmptyLine = true;
+
+    addProperty(res, 'cfg', cluster, 'peerClassLoadingEnabled');
+    addMultiparamProperty(res, 'cfg', cluster, 'peerClassLoadingLocalClassPathExclude');
+    addProperty(res, 'cfg', cluster, 'peerClassLoadingMissedResourcesCacheSize');
+    addProperty(res, 'cfg', cluster, 'peerClassLoadingThreadPoolSize');
+    res.needEmptyLine = true;
+
+    if (cluster.swapSpaceSpi && cluster.swapSpaceSpi.kind == 'FileSwapSpaceSpi') {
+        addBeanWithProperties(res, 'cfg', cluster.swapSpaceSpi.FileSwapSpaceSpi, 'swapSpaceSpi', 'swapSpi',
+            $generatorCommon.SWAP_SPACE_SPI.className, $generatorCommon.SWAP_SPACE_SPI.fields, true);
+
+        res.needEmptyLine = true;
+    }
+
+    addProperty(res, 'cfg', cluster, 'clockSyncSamples');
+    addProperty(res, 'cfg', cluster, 'clockSyncFrequency');
+    addProperty(res, 'cfg', cluster, 'timeServerPortBase');
+    addProperty(res, 'cfg', cluster, 'timeServerPortRange');
+
+    res.needEmptyLine = true;
+
+    addProperty(res, 'cfg', cluster, 'publicThreadPoolSize');
+    addProperty(res, 'cfg', cluster, 'systemThreadPoolSize');
+    addProperty(res, 'cfg', cluster, 'managementThreadPoolSize');
+    addProperty(res, 'cfg', cluster, 'igfsThreadPoolSize');
+
+    res.needEmptyLine = true;
+
+    addBeanWithProperties(res, 'cfg', cluster.transactionConfiguration, 'transactionConfiguration',
+        'transactionConfiguration', $generatorCommon.TRANSACTION_CONFIGURATION.className,
+        $generatorCommon.TRANSACTION_CONFIGURATION.fields);
+
+    res.needEmptyLine = true;
+
+    addProperty(res, 'cfg', cluster, 'cacheSanityCheckEnabled');
+
+    res.needEmptyLine = true;
+
+    if (javaClass) {
+        res.line();
+        res.line('return cfg;');
+        res.endBlock('}');
+        res.endBlock('}');
+
+        return res.generateImports() + '\n\n' + res.join('')
+    }
+
+    return res.join('');
+};
+
+if (typeof window === 'undefined') {
+    module.exports = $generatorJava;
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b72d82a/modules/control-center-web/src/main/js/routes/generator/generator-properties.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/routes/generator/generator-properties.js b/modules/control-center-web/src/main/js/routes/generator/generator-properties.js
new file mode 100644
index 0000000..d26d971
--- /dev/null
+++ b/modules/control-center-web/src/main/js/routes/generator/generator-properties.js
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ */
+
+if (typeof window === 'undefined') {
+    $dataStructures = require('../../helpers/data-structures');
+}
+
+function _addLeadingZero(numberStr, minSize) {
+    if (typeof (numberStr) != 'string')
+        numberStr = '' + numberStr;
+
+    while (numberStr.length < minSize) {
+        numberStr = '0' + numberStr;
+    }
+
+    return numberStr;
+}
+
+function _formatDate(date) {
+    var dd = _addLeadingZero(date.getDate(), 2);
+    var mm = _addLeadingZero(date.getMonth() + 1, 2);
+
+    var yyyy = date.getFullYear();
+
+    return mm + '/' + dd + '/' + yyyy + ' ' + _addLeadingZero(date.getHours(), 2) + ':' + _addLeadingZero(date.getMinutes(), 2);
+}
+
+$generatorCommon = {};
+
+$generatorCommon.isDefined = function (v) {
+    return !(v === undefined || v === null);
+};
+
+$generatorCommon.hasProperty = function (obj, props) {
+    for (var propName in props) {
+        if (props.hasOwnProperty(propName)) {
+            if (obj[propName])
+                return true;
+        }
+    }
+
+    return false;
+};
+
+$generatorCommon.mainComment = function mainComment() {
+    return 'This configuration was generated by Ignite Control Center (' + _formatDate(new Date()) + ')';
+};
+
+$generatorCommon.builder = function () {
+    var res = [];
+
+    res.deep = 0;
+    res.lineStart = true;
+
+    res.append = function (s) {
+        if (this.lineStart) {
+            for (var i = 0; i < this.deep; i++)
+                this.push('    ');
+
+            this.lineStart = false;
+        }
+
+        this.push(s);
+
+        return this;
+    };
+
+    res.line = function (s) {
+        if (s)
+            this.append(s);
+
+        this.push('\n');
+        this.lineStart = true;
+
+        return this;
+    };
+
+    res.startBlock = function (s) {
+        if (s)
+            this.append(s);
+
+        this.push('\n');
+        this.lineStart = true;
+        this.deep++;
+
+        return this;
+    };
+
+    res.endBlock = function (s) {
+        this.deep--;
+
+        if (s)
+            this.append(s);
+
+        this.push('\n');
+        this.lineStart = true;
+
+        return this;
+    };
+
+    res.emptyLineIfNeeded = function () {
+        if (this.needEmptyLine) {
+            this.line();
+
+            this.needEmptyLine = false;
+
+            return true;
+        }
+
+        return false;
+    };
+
+    res.imports = {};
+
+    res.importClass = function (clsName) {
+        var fullClassName = $dataStructures.fullClassName(clsName);
+
+        var dotIdx = fullClassName.lastIndexOf('.');
+
+        var shortName = dotIdx > 0 ? fullClassName.substr(dotIdx + 1) : fullClassName;
+
+        if (this.imports[shortName]) {
+            if (this.imports[shortName] != fullClassName)
+                return fullClassName; // Short class names conflict. Return full name.
+        }
+        else
+            this.imports[shortName] = fullClassName;
+
+        return shortName;
+    };
+
+    /**
+     * @returns String with "java imports" section.
+     */
+    res.generateImports = function () {
+        var res = [];
+
+        for (var clsName in this.imports) {
+            if (this.imports.hasOwnProperty(clsName) && this.imports[clsName].lastIndexOf('java.lang.', 0) != 0)
+                res.push('import ' + this.imports[clsName] + ';');
+        }
+
+        res.sort();
+
+        return res.join('\n')
+    };
+
+    return res;
+};
+
+function _classDescriptor(className, fields) {
+    return {className: className, fields: fields}
+}
+
+$generatorCommon.EVICTION_POLICIES = {
+    LRU: _classDescriptor('org.apache.ignite.cache.eviction.lru.LruEvictionPolicy',
+        {batchSize: null, maxMemorySize: null, maxSize: null}),
+    RND: _classDescriptor('org.apache.ignite.cache.eviction.random.RandomEvictionPolicy',
+        {maxSize: null}),
+    FIFO: _classDescriptor('org.apache.ignite.cache.eviction.fifo.FifoEvictionPolicy',
+        {batchSize: null, maxMemorySize: null, maxSize: null}),
+    SORTED: _classDescriptor('org.apache.ignite.cache.eviction.sorted.SortedEvictionPolicy',
+        {batchSize: null, maxMemorySize: null, maxSize: null})
+};
+
+$generatorCommon.MARSHALLERS = {
+    OptimizedMarshaller: _classDescriptor('org.apache.ignite.marshaller.optimized.OptimizedMarshaller',
+        {poolSize: null, requireSerializable: null }),
+    JdkMarshaller: _classDescriptor('org.apache.ignite.marshaller.jdk.JdkMarshaller', {})
+};
+
+$generatorCommon.KNOWN_CLASSES = {
+    Oracle: _classDescriptor('org.apache.ignite.cache.store.jdbc.dialect.OracleDialect', {}),
+    DB2: _classDescriptor('org.apache.ignite.cache.store.jdbc.dialect.DB2Dialect', {}),
+    SQLServer: _classDescriptor('org.apache.ignite.cache.store.jdbc.dialect.SQLServerDialect', {}),
+    MySQL: _classDescriptor('org.apache.ignite.cache.store.jdbc.dialect.MySQLDialect', {}),
+    PostgreSQL: _classDescriptor('org.apache.ignite.cache.store.jdbc.dialect.BasicJdbcDialect', {}),
+    H2: _classDescriptor('org.apache.ignite.cache.store.jdbc.dialect.H2Dialect', {})
+};
+
+$generatorCommon.DATA_SOURCES = {
+    Oracle: 'oracle.jdbc.pool.OracleDataSource',
+    DB2: 'com.ibm.db2.jcc.DB2ConnectionPoolDataSource',
+    SQLServer: 'com.microsoft.sqlserver.jdbc.SQLServerDataSource',
+    MySQL: 'com.mysql.jdbc.jdbc2.optional.MysqlDataSource',
+    PostgreSQL: 'org.postgresql.ds.PGPoolingDataSource',
+    H2: 'org.h2.jdbcx.JdbcDataSource'
+};
+
+$generatorCommon.STORE_FACTORIES = {
+    CacheJdbcPojoStoreFactory: _classDescriptor('org.apache.ignite.cache.store.jdbc.CacheJdbcPojoStoreFactory',
+        {dataSourceBean: null, dialect: {type: 'className'}}),
+    CacheJdbcBlobStoreFactory: _classDescriptor('org.apache.ignite.cache.store.jdbc.CacheJdbcBlobStoreFactory',
+        {user: null, dataSourceBean: null, initSchema: null, createTableQuery: null, loadQuery: null, insertQuery: null, updateQuery: null, deleteQuery: null}),
+    CacheHibernateBlobStoreFactory: _classDescriptor('org.apache.ignite.cache.store.hibernate.CacheHibernateBlobStoreFactory',
+        {hibernateProperties: {type: 'propertiesAsList', propVarName: 'props'}})
+};
+
+$generatorCommon.ATOMIC_CONFIGURATION = _classDescriptor('org.apache.ignite.configuration.AtomicConfiguration',
+    {backups: null, cacheMode: {type: 'enum', enumClass: 'CacheMode'}, atomicSequenceReserveSize: null});
+
+$generatorCommon.SWAP_SPACE_SPI = _classDescriptor('org.apache.ignite.spi.swapspace.file.FileSwapSpaceSpi',
+    {baseDirectory: null, readStripesNumber: null, maximumSparsity: {type: 'float'}, maxWriteQueueSize: null, writeBufferSize: null});
+
+$generatorCommon.TRANSACTION_CONFIGURATION = _classDescriptor('org.apache.ignite.configuration.TransactionConfiguration',
+    {
+        defaultTxConcurrency: {type: 'enum', enumClass: 'TransactionConcurrency'},
+        transactionIsolation: {type: 'TransactionIsolation', setterName: 'defaultTxIsolation'},
+        defaultTxTimeout: null,
+        pessimisticTxLogLinger: null,
+        pessimisticTxLogSize: null,
+        txSerializableEnabled: null}
+);
+
+/**
+ * Generate properties file with properties stubs for stores data sources.
+ *
+ * @param cluster Configuration to process.
+ * @returns {string} Generated content.
+ */
+$generatorCommon.generateProperties = function (cluster) {
+    var res = exports.builder();
+
+    var datasources = [];
+
+    if (cluster.caches && cluster.caches.length > 0) {
+        _.forEach(cluster.caches, function (cache) {
+            if (cache.cacheStoreFactory && cache.cacheStoreFactory.kind) {
+                var storeFactory = cache.cacheStoreFactory[cache.cacheStoreFactory.kind];
+
+                if (storeFactory.dialect) {
+                    var beanId = storeFactory.dataSourceBean;
+
+                    if (!_.contains(datasources, beanId)) {
+                        datasources.push(beanId);
+
+                        res.line(beanId + '.jdbc.url=YOUR_JDBC_URL');
+                        res.line(beanId + '.jdbc.username=YOUR_USER_NAME');
+                        res.line(beanId + '.jdbc.password=YOUR_PASSWORD');
+                        res.line();
+                    }
+                }
+            }
+        });
+    }
+
+    if (datasources.length > 0)
+        return '# ' + mainComment() + '\n\n' + res.join('');
+
+    return undefined;
+};
+
+if (typeof window === 'undefined') {
+    module.exports = $generatorCommon;
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/0b72d82a/modules/control-center-web/src/main/js/routes/generator/generator-xml.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/routes/generator/generator-xml.js b/modules/control-center-web/src/main/js/routes/generator/generator-xml.js
new file mode 100644
index 0000000..29959a7
--- /dev/null
+++ b/modules/control-center-web/src/main/js/routes/generator/generator-xml.js
@@ -0,0 +1,896 @@
+/*
+ * 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.
+ */
+
+if (typeof window === 'undefined') {
+    _ = require('lodash');
+
+    $commonUtils = require('../../helpers/common-utils');
+    $dataStructures = require('../../helpers/data-structures');
+    $generatorCommon = require('./generator-common');
+}
+
+function _escape(s) {
+    if (typeof(s) != 'string')
+        return s;
+
+    return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+}
+
+function _escapeAttr(s) {
+    if (typeof(s) != 'string')
+        return s;
+
+    return s.replace(/&/g, '&amp;').replace(/"/g, '&quot;');
+}
+
+function _addProperty(res, obj, propName, setterName) {
+    var val = obj[propName];
+
+    if ($commonUtils.isDefined(val))
+        _addElement(res, 'property', 'name', setterName ? setterName : propName, 'value', _escapeAttr(val));
+
+    return val;
+}
+
+function _addClassNameProperty(res, obj, propName) {
+    var val = obj[propName];
+
+    if ($commonUtils.isDefined(val))
+        _addElement(res, 'property', 'name', propName, 'value', $generatorCommon.javaBuildInClass(val));
+
+    return val;
+}
+
+function _addListProperty(res, obj, propName, listType, rowFactory) {
+    var val = obj[propName];
+
+    if (val && val.length > 0) {
+        res.emptyLineIfNeeded();
+
+        if (!listType)
+            listType = 'list';
+
+        if (!rowFactory)
+            rowFactory = function (val) {
+                return '<value>' + escape(val) + '</value>'
+            };
+
+        res.startBlock('<property name="' + propName + '">');
+        res.startBlock('<' + listType + '>');
+
+        for (var i = 0; i < val.length; i++)
+            res.line(rowFactory(val[i]));
+
+        res.endBlock('</' + listType + '>');
+        res.endBlock('</property>');
+    }
+}
+
+function _addBeanWithProperties(res, bean, beanPropName, beanClass, props, createBeanAlthoughNoProps) {
+    if (bean && $commonUtils.hasProperty(bean, props)) {
+        res.emptyLineIfNeeded();
+        res.startBlock('<property name="' + beanPropName + '">');
+        res.startBlock('<bean class="' + beanClass + '">');
+
+        for (var propName in props) {
+            if (props.hasOwnProperty(propName)) {
+                var descr = props[propName];
+
+                if (descr) {
+                    if (descr.type == 'list') {
+                        _addListProperty(res, bean, propName, descr.setterName);
+                    }
+                    else if (descr.type == 'className') {
+                        if (bean[propName]) {
+                            res.startBlock('<property name="' + propName + '">');
+                            res.line('<bean class="' + $generatorCommon.KNOWN_CLASSES[bean[propName]].className + '"/>');
+                            res.endBlock('</property>');
+                        }
+                    }
+                    else if (descr.type == 'propertiesAsList') {
+                        var val = bean[propName];
+
+                        if (val && val.length > 0) {
+                            res.startBlock('<property name="' + propName + '">');
+                            res.startBlock('<props>');
+
+                            for (var i = 0; i < val.length; i++) {
+                                var nameAndValue = val[i];
+
+                                var eqIndex = nameAndValue.indexOf('=');
+                                if (eqIndex >= 0) {
+                                    res.line('<prop key="' + _escapeAttr(nameAndValue.substring(0, eqIndex)) + '">' +
+                                        escape(nameAndValue.substr(eqIndex + 1)) + '</prop>');
+                                }
+                            }
+
+                            res.endBlock('</props>');
+                            res.endBlock('</property>');
+                        }
+                    }
+                    else
+                        _addProperty(res, bean, propName, descr.setterName);
+                }
+                else
+                    _addProperty(res, bean, propName);
+            }
+        }
+
+        res.endBlock('</bean>');
+        res.endBlock('</property>');
+    }
+    else if (createBeanAlthoughNoProps) {
+        res.emptyLineIfNeeded();
+        res.line('<property name="' + beanPropName + '">');
+        res.line('    <bean class="' + beanClass + '"/>');
+        res.line('</property>');
+    }
+}
+
+function _createEvictionPolicy(res, evictionPolicy, propertyName) {
+    if (evictionPolicy && evictionPolicy.kind) {
+        var e = $generatorCommon.evictionPolicies[evictionPolicy.kind];
+
+        var obj = evictionPolicy[evictionPolicy.kind.toUpperCase()];
+
+        _addBeanWithProperties(res, obj, propertyName, e.className, e.fields, true);
+    }
+}
+
+function _addCacheTypeMetadataDatabaseFields(res, meta, fieldProperty) {
+    var fields = meta[fieldProperty];
+
+    if (fields && fields.length > 0) {
+        res.startBlock('<property name="' + fieldProperty + '">');
+
+        res.startBlock('<list>');
+
+        _.forEach(fields, function (field) {
+            res.startBlock('<bean class="org.apache.ignite.cache.CacheTypeFieldMetadata">');
+
+            _addProperty(res, field, 'databaseName');
+
+            res.startBlock('<property name="databaseType">');
+            res.line('<util:constant static-field="java.sql.Types.' + field.databaseType + '"/>');
+            res.endBlock('</property>');
+
+            _addProperty(res, field, 'javaName');
+
+            _addClassNameProperty(res, field, 'javaType');
+
+            res.endBlock('</bean>');
+        });
+
+        res.endBlock('</list>');
+        res.endBlock('</property>');
+    }
+}
+
+function _addCacheTypeMetadataQueryFields(res, meta, fieldProperty) {
+    var fields = meta[fieldProperty];
+
+    if (fields && fields.length > 0) {
+        res.startBlock('<property name="' + fieldProperty + '">');
+
+        res.startBlock('<map>');
+
+        _.forEach(fields, function (field) {
+            _addElement(res, 'entry', 'key', field.name, 'value', $generatorCommon.javaBuildInClass(field.className));
+        });
+
+        res.endBlock('</map>');
+
+        res.endBlock('</property>');
+    }
+}
+
+function _addCacheTypeMetadataGroups(res, meta) {
+    var groups = meta.groups;
+
+    if (groups && groups.length > 0) {
+        res.startBlock('<property name="groups">');
+        res.startBlock('<map>');
+
+        _.forEach(groups, function (group) {
+            var fields = group.fields;
+
+            if (fields && fields.length > 0) {
+                res.startBlock('<entry key="' + group.name + '">');
+                res.startBlock('<map>');
+
+                _.forEach(fields, function (field) {
+                    res.startBlock('<entry key="' + field.name + '">');
+
+                    res.startBlock('<bean class="org.apache.ignite.lang.IgniteBiTuple">');
+                    res.line('<constructor-arg value="' + $generatorCommon.javaBuildInClass(field.className) + '"/>');
+                    res.line('<constructor-arg value="' + field.direction + '"/>');
+                    res.endBlock('</bean>');
+
+                    res.endBlock('</entry>');
+                });
+
+                res.endBlock('</map>');
+                res.endBlock('</entry>');
+            }
+        });
+
+        res.endBlock('</map>');
+        res.endBlock('</property>');
+    }
+}
+
+function _generateCacheTypeMetadataConfiguration(res, meta) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    res.startBlock('<bean class="org.apache.ignite.cache.CacheTypeMetadata">');
+
+    var kind = meta.kind;
+
+    var keyType = addClassNameProperty(res, meta, 'keyType');
+
+    _addProperty(res, meta, 'valueType');
+
+    if (kind != 'query') {
+        _addProperty(res, meta, 'databaseSchema');
+        _addProperty(res, meta, 'databaseTable');
+
+        if (!generatorCommon.isJavaBuildInClass(keyType))
+            addCacheTypeMetadataDatabaseFields(res, meta, 'keyFields');
+
+        addCacheTypeMetadataDatabaseFields(res, meta, 'valueFields');
+    }
+
+    if (kind != 'store') {
+        addCacheTypeMetadataQueryFields(res, meta, 'queryFields');
+        addCacheTypeMetadataQueryFields(res, meta, 'ascendingFields');
+        addCacheTypeMetadataQueryFields(res, meta, 'descendingFields');
+
+        _addListProperty(res, meta, 'textFields');
+
+        addCacheTypeMetadataGroups(res, meta);
+    }
+
+    res.endBlock('</bean>');
+
+    return res;
+}
+
+function _generateCacheConfiguration(res, cacheCfg) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    res.startBlock('<bean class="org.apache.ignite.configuration.CacheConfiguration">');
+
+    _addProperty(res, cacheCfg, 'name');
+
+    res.needEmptyLine = true;
+
+    var cacheMode = _addProperty(res, cacheCfg, 'mode', 'cacheMode');
+
+    _addProperty(res, cacheCfg, 'atomicityMode');
+
+    if (cacheMode == 'PARTITIONED')
+        _addProperty(res, cacheCfg, 'backups');
+
+    _addProperty(res, cacheCfg, 'readFromBackup');
+
+    _addProperty(res, cacheCfg, 'startSize');
+
+    res.needEmptyLine = true;
+
+    _addProperty(res, cacheCfg, 'memoryMode');
+    _addProperty(res, cacheCfg, 'offHeapMaxMemory');
+    _addProperty(res, cacheCfg, 'swapEnabled');
+    _addProperty(res, cacheCfg, 'copyOnRead');
+
+    res.needEmptyLine = true;
+
+    _createEvictionPolicy(res, cacheCfg.evictionPolicy, 'evictionPolicy');
+
+    res.needEmptyLine = true;
+
+    if (cacheCfg.nearCacheEnabled) {
+        res.emptyLineIfNeeded();
+
+        res.startBlock('<property name="nearConfiguration">');
+        res.startBlock('<bean class="org.apache.ignite.configuration.NearCacheConfiguration">');
+
+        if (cacheCfg.nearConfiguration && cacheCfg.nearConfiguration.nearStartSize)
+            _addProperty(res, cacheCfg.nearConfiguration, 'nearStartSize');
+
+        if (cacheCfg.nearConfiguration && cacheCfg.nearConfiguration.nearEvictionPolicy.kind)
+            _createEvictionPolicy(res, cacheCfg.nearConfiguration.nearEvictionPolicy, 'nearEvictionPolicy');
+
+        res.endBlock('</bean>');
+        res.endBlock('</property>');
+    }
+
+    res.needEmptyLine = true;
+
+    _addProperty(res, cacheCfg, 'sqlEscapeAll');
+    _addProperty(res, cacheCfg, 'sqlOnheapRowCacheSize');
+    _addProperty(res, cacheCfg, 'longQueryWarningTimeout');
+
+    if (cacheCfg.indexedTypes && cacheCfg.indexedTypes.length > 0) {
+        res.startBlock('<property name="indexedTypes">');
+        res.startBlock('<list>');
+
+        for (var i = 0; i < cacheCfg.indexedTypes.length; i++) {
+            var pair = cacheCfg.indexedTypes[i];
+
+            res.line('<value>' + $generatorCommon.javaBuildInClass(pair.keyClass) + '</value>');
+            res.line('<value>' + $generatorCommon.javaBuildInClass(pair.valueClass) + '</value>');
+        }
+
+        res.endBlock('</list>');
+        res.endBlock('</property>');
+    }
+
+    _addListProperty(res, cacheCfg, 'sqlFunctionClasses', 'array');
+
+    res.needEmptyLine = true;
+
+    if (cacheMode != 'LOCAL') {
+        _addProperty(res, cacheCfg, 'rebalanceMode');
+        _addProperty(res, cacheCfg, 'rebalanceThreadPoolSize');
+        _addProperty(res, cacheCfg, 'rebalanceBatchSize');
+        _addProperty(res, cacheCfg, 'rebalanceOrder');
+        _addProperty(res, cacheCfg, 'rebalanceDelay');
+        _addProperty(res, cacheCfg, 'rebalanceTimeout');
+        _addProperty(res, cacheCfg, 'rebalanceThrottle');
+
+        res.needEmptyLine = true;
+    }
+
+    if (cacheCfg.cacheStoreFactory && cacheCfg.cacheStoreFactory.kind) {
+        var storeFactory = cacheCfg.cacheStoreFactory[cacheCfg.cacheStoreFactory.kind];
+        var data = $generatorCommon.STORE_FACTORIES[cacheCfg.cacheStoreFactory.kind];
+
+        _addBeanWithProperties(res, storeFactory, 'cacheStoreFactory', data.className, data.fields, true);
+
+        if (storeFactory.dialect) {
+            if (_.findIndex(res.datasources, function (ds) {
+                    return ds.dataSourceBean == storeFactory.dataSourceBean;
+                }) < 0) {
+                res.datasources.push({
+                    dataSourceBean: storeFactory.dataSourceBean,
+                    className: $generatorCommon.DATA_SOURCES[storeFactory.dialect]
+                });
+            }
+        }
+    }
+
+    res.needEmptyLine = true;
+
+    _addProperty(res, cacheCfg, 'loadPreviousValue');
+    _addProperty(res, cacheCfg, 'readThrough');
+    _addProperty(res, cacheCfg, 'writeThrough');
+
+    res.needEmptyLine = true;
+
+    _addProperty(res, cacheCfg, 'invalidate');
+    _addProperty(res, cacheCfg, 'defaultLockTimeout');
+    _addProperty(res, cacheCfg, 'transactionManagerLookupClassName');
+
+    res.needEmptyLine = true;
+
+    _addProperty(res, cacheCfg, 'writeBehindEnabled');
+    _addProperty(res, cacheCfg, 'writeBehindBatchSize');
+    _addProperty(res, cacheCfg, 'writeBehindFlushSize');
+    _addProperty(res, cacheCfg, 'writeBehindFlushFrequency');
+    _addProperty(res, cacheCfg, 'writeBehindFlushThreadCount');
+
+    res.needEmptyLine = true;
+
+    _addProperty(res, cacheCfg, 'statisticsEnabled');
+    _addProperty(res, cacheCfg, 'managementEnabled');
+
+    res.needEmptyLine = true;
+
+    _addProperty(res, cacheCfg, 'maxConcurrentAsyncOperations');
+
+    // Generate cache type metadata configs.
+    if ((cacheCfg.queryMetadata && cacheCfg.queryMetadata.length > 0) ||
+        (cacheCfg.storeMetadata && cacheCfg.storeMetadata.length > 0)) {
+        res.emptyLineIfNeeded();
+
+        res.startBlock('<property name="typeMetadata">');
+        res.startBlock('<list>');
+
+        var metaNames = [];
+
+        if (cacheCfg.queryMetadata && cacheCfg.queryMetadata.length > 0) {
+            _.forEach(cacheCfg.queryMetadata, function (meta) {
+                if (!_.contains(metaNames, meta.name)) {
+                    metaNames.push(meta.name);
+
+                    _generateCacheTypeMetadataConfiguration(res, meta);
+                }
+            });
+        }
+
+        if (cacheCfg.storeMetadata && cacheCfg.storeMetadata.length > 0) {
+            _.forEach(cacheCfg.storeMetadata, function (meta) {
+                if (!_.contains(metaNames, meta.name)) {
+                    metaNames.push(meta.name);
+
+                    _generateCacheTypeMetadataConfiguration(res, meta);
+                }
+            });
+        }
+
+        res.endBlock('</list>');
+        res.endBlock('</property>');
+    }
+
+    res.endBlock('</bean>');
+
+    return res;
+}
+
+function _addElement(res, tag, attr1, val1, attr2, val2) {
+    var elem = '<' + tag;
+
+    if (attr1) {
+        elem += ' ' + attr1 + '="' + val1 + '"'
+    }
+
+    if (attr2) {
+        elem += ' ' + attr2 + '="' + val2 + '"'
+    }
+
+    elem += '/>';
+
+    res.emptyLineIfNeeded();
+    res.line(elem);
+}
+
+$generatorXml = {};
+
+$generatorXml.general = function (cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    // Generate discovery.
+    if (cluster.discovery) {
+        res.startBlock('<property name="discoverySpi">');
+        res.startBlock('<bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">');
+        res.startBlock('<property name="ipFinder">');
+
+        var d = cluster.discovery;
+
+        switch (d.kind) {
+            case 'Multicast':
+                res.startBlock('<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder">');
+
+                _addProperty(res, d.Multicast, 'multicastGroup');
+                _addProperty(res, d.Multicast, 'multicastPort');
+                _addProperty(res, d.Multicast, 'responseWaitTime');
+                _addProperty(res, d.Multicast, 'addressRequestAttempts');
+                _addProperty(res, d.Multicast, 'localAddress');
+
+                res.endBlock('</bean>');
+
+                break;
+
+            case 'Vm':
+                if (d.Vm.addresses.length > 0) {
+                    res.startBlock('<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">');
+
+                    _addListProperty(res, d.Vm, 'addresses');
+
+                    res.endBlock('</bean>');
+                }
+                else {
+                    res.line('<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder"/>');
+                }
+
+                break;
+
+            case 'S3':
+                res.startBlock('<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.s3.TcpDiscoveryS3IpFinder">');
+
+                if (d.S3 && d.S3.bucketName)
+                    res.line('<property name="bucketName" value="' + _escapeAttr(d.S3.bucketName) + '" />');
+
+                res.endBlock('</bean>');
+
+                break;
+
+            case 'Cloud':
+                res.startBlock('<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.cloud.TcpDiscoveryCloudIpFinder">');
+
+                _addProperty(res, d.Cloud, 'credential');
+                _addProperty(res, d.Cloud, 'credentialPath');
+                _addProperty(res, d.Cloud, 'identity');
+                _addProperty(res, d.Cloud, 'provider');
+                _addListProperty(res, d.Cloud, 'regions');
+                _addListProperty(res, d.Cloud, 'zones');
+
+                res.endBlock('</bean>');
+
+                break;
+
+            case 'GoogleStorage':
+                res.startBlock('<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.gce.TcpDiscoveryGoogleStorageIpFinder">');
+
+                _addProperty(res, d.GoogleStorage, 'projectName');
+                _addProperty(res, d.GoogleStorage, 'bucketName');
+                _addProperty(res, d.GoogleStorage, 'serviceAccountP12FilePath');
+                _addProperty(res, d.GoogleStorage, 'serviceAccountId');
+
+                //if (d.GoogleStorage.addrReqAttempts) todo ????
+                //    res.line('<property name="serviceAccountP12FilePath" value="' + _escapeAttr(d.GoogleStorage.addrReqAttempts) + '"/>');
+
+                res.endBlock('</bean>');
+
+                break;
+
+            case 'Jdbc':
+                res.startBlock('<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.jdbc.TcpDiscoveryJdbcIpFinder">');
+                res.line('<property name="initSchema" value="' + ($commonUtils.isDefined(d.Jdbc.initSchema) && d.Jdbc.initSchema) + '"/>');
+                res.endBlock('</bean>');
+
+                break;
+
+            case 'SharedFs':
+                if (d.SharedFs.path) {
+                    res.startBlock('<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.sharedfs.TcpDiscoverySharedFsIpFinder">');
+                    _addProperty(res, d.SharedFs, 'path');
+                    res.endBlock('</bean>');
+                }
+                else {
+                    res.line('<bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.sharedfs.TcpDiscoverySharedFsIpFinder"/>');
+                }
+
+                break;
+
+            default:
+                throw "Unknown discovery kind: " + d.kind;
+        }
+
+        res.endBlock('</property>');
+        res.endBlock('</bean>');
+        res.endBlock('</property>');
+
+        res.needEmptyLine = true;
+    }
+
+    return res;
+};
+
+// Generate atomics group.
+$generatorXml.atomics = function (cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    var atomicCfg = $generatorCommon.ATOMIC_CONFIGURATION;
+
+    _addBeanWithProperties(res, cluster.atomicConfiguration, 'atomicConfiguration', atomicCfg.className, atomicCfg.fields);
+
+    res.needEmptyLine = true;
+
+    return res;
+};
+
+// Generate communication group.
+$generatorXml.communication = function (cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    _addProperty(res, cluster, 'networkTimeout');
+    _addProperty(res, cluster, 'networkSendRetryDelay');
+    _addProperty(res, cluster, 'networkSendRetryCount');
+    _addProperty(res, cluster, 'segmentCheckFrequency');
+    _addProperty(res, cluster, 'waitForSegmentOnStart');
+    _addProperty(res, cluster, 'discoveryStartupDelay');
+
+    res.needEmptyLine = true;
+
+    return res;
+};
+
+// Generate deployment group.
+$generatorXml.deployment = function (cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    _addProperty(res, cluster, 'deploymentMode');
+
+    res.needEmptyLine = true;
+
+    return res;
+};
+
+// Generate events group.
+$generatorXml.events = function (cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    if (cluster.includeEventTypes && cluster.includeEventTypes.length > 0) {
+        res.emptyLineIfNeeded();
+
+        res.startBlock('<property name="includeEventTypes">');
+
+        if (cluster.includeEventTypes.length == 1)
+            res.line('<util:constant static-field="org.apache.ignite.events.EventType.' + cluster.includeEventTypes[0] + '"/>');
+        else {
+            res.startBlock('<array>');
+
+            for (i = 0; i < cluster.includeEventTypes.length; i++) {
+                if (i > 0)
+                    res.line();
+
+                var eventGroup = cluster.includeEventTypes[i];
+
+                res.line('<!-- EventType.' + eventGroup + ' -->');
+
+                var eventList = $dataStructures.EVENT_GROUPS[eventGroup];
+
+                for (var k = 0; k < eventList.length; k++) {
+                    res.line('<util:constant static-field="org.apache.ignite.events.EventType.' + eventList[k] + '"/>')
+                }
+            }
+
+            res.endBlock('</array>');
+        }
+
+        res.endBlock('</property>');
+
+        res.needEmptyLine = true;
+    }
+
+    return res;
+};
+
+// Generate marshaller group.
+$generatorXml.marshaller = function (cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    var marshaller = cluster.marshaller;
+
+    if (marshaller && marshaller.kind) {
+        var marshallerDesc = $generatorCommon.MARSHALLERS[marshaller.kind];
+
+        _addBeanWithProperties(res, marshaller[marshaller.kind], 'marshaller', marshallerDesc.className, marshallerDesc.fields, true);
+
+        res.needEmptyLine = true;
+    }
+
+    _addProperty(res, cluster, 'marshalLocalJobs');
+    _addProperty(res, cluster, 'marshallerCacheKeepAliveTime');
+    _addProperty(res, cluster, 'marshallerCacheThreadPoolSize');
+
+    res.needEmptyLine = true;
+
+    return res;
+};
+
+// Generate metrics group.
+$generatorXml.metrics = function (cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    _addProperty(res, cluster, 'metricsExpireTime');
+    _addProperty(res, cluster, 'metricsHistorySize');
+    _addProperty(res, cluster, 'metricsLogFrequency');
+    _addProperty(res, cluster, 'metricsUpdateFrequency');
+
+    res.needEmptyLine = true;
+
+    return res;
+};
+
+// Generate PeerClassLoading group.
+$generatorXml.p2p = function (cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    _addProperty(res, cluster, 'peerClassLoadingEnabled');
+    _addListProperty(res, cluster, 'peerClassLoadingLocalClassPathExclude');
+    _addProperty(res, cluster, 'peerClassLoadingMissedResourcesCacheSize');
+    _addProperty(res, cluster, 'peerClassLoadingThreadPoolSize');
+
+    res.needEmptyLine = true;
+
+    return res;
+};
+
+// Generate swap group.
+$generatorXml.swap = function (cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    var swapSpaceSpi = $generatorCommon.SWAP_SPACE_SPI;
+
+    if (cluster.swapSpaceSpi && cluster.swapSpaceSpi.kind == 'FileSwapSpaceSpi') {
+        _addBeanWithProperties(res, cluster.swapSpaceSpi.FileSwapSpaceSpi, 'swapSpaceSpi',
+            swapSpaceSpi.className, swapSpaceSpi.fields, true);
+
+        res.needEmptyLine = true;
+    }
+
+    return res;
+};
+
+// Generate time group.
+$generatorXml.time = function (cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    _addProperty(res, cluster, 'clockSyncSamples');
+    _addProperty(res, cluster, 'clockSyncFrequency');
+    _addProperty(res, cluster, 'timeServerPortBase');
+    _addProperty(res, cluster, 'timeServerPortRange');
+
+    res.needEmptyLine = true;
+
+    return res;
+};
+
+// Generate thread pools group.
+$generatorXml.pools = function (cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    _addProperty(res, cluster, 'publicThreadPoolSize');
+    _addProperty(res, cluster, 'systemThreadPoolSize');
+    _addProperty(res, cluster, 'managementThreadPoolSize');
+    _addProperty(res, cluster, 'igfsThreadPoolSize');
+    res.needEmptyLine = true;
+
+    return res;
+};
+
+// Generate transactions group.
+$generatorXml.transactions = function (cluster, res) {
+    if (!res)
+        res = $generatorCommon.builder();
+
+    var trnCfg = $generatorCommon.TRANSACTION_CONFIGURATION;
+
+    _addBeanWithProperties(res, cluster.transactionConfiguration, 'transactionConfiguration', trnCfg.className, trnCfg.fields);
+
+    res.needEmptyLine = true;
+
+    return res;
+};
+
+$generatorXml.clusterConfiguration = function (cluster, clientNearConfiguration) {
+    var res = $generatorCommon.builder();
+
+    res.datasources = [];
+    res.deep = 1;
+
+    if (clientNearConfiguration) {
+        res.startBlock('<bean id="nearCacheBean" class="org.apache.ignite.configuration.NearCacheConfiguration">');
+
+        if (clientNearConfiguration.nearStartSize)
+            _addProperty(res, clientNearConfiguration, 'nearStartSize');
+
+        if (clientNearConfiguration.nearEvictionPolicy && clientNearConfiguration.nearEvictionPolicy.kind)
+            _createEvictionPolicy(res, clientNearConfiguration.nearEvictionPolicy, 'nearEvictionPolicy');
+
+        res.endBlock('</bean>');
+
+        res.line();
+    }
+
+    // Generate Ignite Configuration.
+    res.startBlock('<bean class="org.apache.ignite.configuration.IgniteConfiguration">');
+
+    if (clientNearConfiguration) {
+        res.line('<property name="clientMode" value="true" />');
+
+        res.line();
+    }
+
+    $generatorXml.general(cluster, res);
+
+    $generatorXml.atomics(cluster, res);
+
+    $generatorXml.communication(cluster, res);
+
+    $generatorXml.deployment(cluster, res);
+
+    $generatorXml.events(cluster, res);
+
+    $generatorXml.marshaller(cluster, res);
+
+    $generatorXml.metrics(cluster, res);
+
+    $generatorXml.p2p(cluster, res);
+
+    $generatorXml.swap(cluster, res);
+
+    $generatorXml.time(cluster, res);
+
+    $generatorXml.pools(cluster, res);
+
+    $generatorXml.transactions(cluster, res);
+
+    // Generate caches configs.
+    if (cluster.caches && cluster.caches.length > 0) {
+        res.emptyLineIfNeeded();
+
+        res.startBlock('<property name="cacheConfiguration">');
+        res.startBlock('<list>');
+
+        for (var i = 0; i < cluster.caches.length; i++) {
+            if (i > 0)
+                res.line();
+
+            var cache = cluster.caches[i];
+
+            _generateCacheConfiguration(res, cache);
+        }
+
+        res.endBlock('</list>');
+        res.endBlock('</property>');
+
+        res.needEmptyLine = true;
+    }
+
+    res.endBlock('</bean>');
+
+    // Build final XML:
+    // 1. Add header.
+    var xml = '<?xml version="1.0" encoding="UTF-8"?>\n\n';
+
+    xml += '<!-- ' + $generatorCommon.mainComment() + ' -->\n';
+    xml += '<beans xmlns="http://www.springframework.org/schema/beans"\n';
+    xml += '       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n';
+    xml += '       xmlns:util="http://www.springframework.org/schema/util"\n';
+    xml += '       xsi:schemaLocation="http://www.springframework.org/schema/beans\n';
+    xml += '                           http://www.springframework.org/schema/beans/spring-beans.xsd\n';
+    xml += '                           http://www.springframework.org/schema/util\n';
+    xml += '                           http://www.springframework.org/schema/util/spring-util.xsd">\n';
+
+    // 2. Add external property file and all data sources.
+    if (res.datasources.length > 0) {
+        xml += '    <!-- Load external properties file. -->\n';
+        xml += '    <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">\n';
+        xml += '        <property name="location" value="classpath:secret.properties"/>\n';
+        xml += '    </bean>\n\n';
+
+        xml += '    <!-- Data source beans will be initialized from external properties file. -->\n';
+
+        _.forEach(res.datasources, function (item) {
+            var beanId = item.dataSourceBean;
+
+            xml += '    <bean id= "' + beanId + '" class="' + item.className + '">\n';
+            xml += '        <property name="URL" value="${' + beanId + '.jdbc.url}" />\n';
+            xml += '        <property name="user" value="${' + beanId + '.jdbc.username}" />\n';
+            xml += '        <property name="password" value="${' + beanId + '.jdbc.password}" />\n';
+            xml += '    </bean>\n\n';
+        });
+    }
+
+    // 3. Add main content.
+    xml += res.join('');
+
+    // 4. Add footer.
+    xml += '</beans>\n';
+
+    return xml;
+};
+
+if (typeof window === 'undefined') {
+    module.exports = $generatorXml;
+}