You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by an...@apache.org on 2015/10/13 05:28:15 UTC
[14/18] ignite git commit: IGNITE-843 Web console initial commit.
IGNITE-843 Web console initial commit.
Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/bce0deb7
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/bce0deb7
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/bce0deb7
Branch: refs/heads/ignite-843-rc1
Commit: bce0deb71f5a70868725ea696644f9b1f96cf2b1
Parents: 81962c4
Author: Andrey <an...@gridgain.com>
Authored: Tue Oct 13 10:06:27 2015 +0700
Committer: Andrey <an...@gridgain.com>
Committed: Tue Oct 13 10:06:27 2015 +0700
----------------------------------------------------------------------
.../control-center-web/licenses/apache-2.0.txt | 202 ++
modules/control-center-web/pom.xml | 71 +
.../control-center-web/src/main/js/.gitignore | 4 +
.../control-center-web/src/main/js/DEVNOTES.txt | 21 +
.../src/main/js/agents/agent-manager.js | 312 +++
.../src/main/js/agents/agent-server.js | 98 +
modules/control-center-web/src/main/js/app.js | 190 ++
modules/control-center-web/src/main/js/bin/www | 126 ++
.../src/main/js/config/default.json | 25 +
.../src/main/js/controllers/admin-controller.js | 81 +
.../main/js/controllers/caches-controller.js | 594 ++++++
.../main/js/controllers/clusters-controller.js | 560 +++++
.../src/main/js/controllers/common-module.js | 2017 ++++++++++++++++++
.../main/js/controllers/metadata-controller.js | 1252 +++++++++++
.../src/main/js/controllers/models/caches.json | 1027 +++++++++
.../main/js/controllers/models/clusters.json | 1333 ++++++++++++
.../main/js/controllers/models/metadata.json | 279 +++
.../src/main/js/controllers/models/summary.json | 172 ++
.../main/js/controllers/profile-controller.js | 94 +
.../src/main/js/controllers/sql-controller.js | 1097 ++++++++++
.../main/js/controllers/summary-controller.js | 233 ++
modules/control-center-web/src/main/js/db.js | 431 ++++
.../src/main/js/helpers/common-utils.js | 104 +
.../src/main/js/helpers/configuration-loader.js | 43 +
.../src/main/js/helpers/data-structures.js | 113 +
.../src/main/js/keys/test.crt | 13 +
.../src/main/js/keys/test.key | 18 +
.../control-center-web/src/main/js/package.json | 53 +
.../public/stylesheets/_bootstrap-custom.scss | 67 +
.../stylesheets/_bootstrap-variables.scss | 890 ++++++++
.../src/main/js/public/stylesheets/style.scss | 1838 ++++++++++++++++
.../src/main/js/routes/admin.js | 127 ++
.../src/main/js/routes/agent.js | 261 +++
.../src/main/js/routes/caches.js | 171 ++
.../src/main/js/routes/clusters.js | 145 ++
.../js/routes/generator/generator-common.js | 353 +++
.../js/routes/generator/generator-docker.js | 60 +
.../main/js/routes/generator/generator-java.js | 1581 ++++++++++++++
.../js/routes/generator/generator-properties.js | 112 +
.../main/js/routes/generator/generator-xml.js | 1202 +++++++++++
.../src/main/js/routes/metadata.js | 192 ++
.../src/main/js/routes/notebooks.js | 157 ++
.../src/main/js/routes/presets.js | 70 +
.../src/main/js/routes/profile.js | 105 +
.../src/main/js/routes/public.js | 266 +++
.../src/main/js/routes/sql.js | 39 +
.../src/main/js/routes/summary.js | 104 +
.../src/main/js/views/configuration/caches.jade | 46 +
.../main/js/views/configuration/clusters.jade | 46 +
.../js/views/configuration/metadata-load.jade | 89 +
.../main/js/views/configuration/metadata.jade | 65 +
.../main/js/views/configuration/sidebar.jade | 48 +
.../js/views/configuration/summary-tabs.jade | 24 +
.../main/js/views/configuration/summary.jade | 124 ++
.../src/main/js/views/error.jade | 22 +
.../src/main/js/views/includes/controls.jade | 515 +++++
.../src/main/js/views/includes/footer.jade | 22 +
.../src/main/js/views/includes/header.jade | 40 +
.../src/main/js/views/index.jade | 141 ++
.../src/main/js/views/reset.jade | 38 +
.../src/main/js/views/settings/admin.jade | 57 +
.../src/main/js/views/settings/profile.jade | 68 +
.../src/main/js/views/sql/cache-metadata.jade | 26 +
.../src/main/js/views/sql/chart-settings.jade | 38 +
.../src/main/js/views/sql/notebook-new.jade | 31 +
.../src/main/js/views/sql/paragraph-rate.jade | 31 +
.../src/main/js/views/sql/sql.jade | 173 ++
.../main/js/views/templates/agent-download.jade | 49 +
.../main/js/views/templates/batch-confirm.jade | 32 +
.../src/main/js/views/templates/clone.jade | 32 +
.../src/main/js/views/templates/confirm.jade | 27 +
.../src/main/js/views/templates/layout.jade | 82 +
.../src/main/js/views/templates/message.jade | 26 +
.../src/main/js/views/templates/select.jade | 26 +
.../js/views/templates/validation-error.jade | 25 +
.../src/test/js/routes/agent.js | 96 +
76 files changed, 20342 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ignite/blob/bce0deb7/modules/control-center-web/licenses/apache-2.0.txt
----------------------------------------------------------------------
diff --git a/modules/control-center-web/licenses/apache-2.0.txt b/modules/control-center-web/licenses/apache-2.0.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/modules/control-center-web/licenses/apache-2.0.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
http://git-wip-us.apache.org/repos/asf/ignite/blob/bce0deb7/modules/control-center-web/pom.xml
----------------------------------------------------------------------
diff --git a/modules/control-center-web/pom.xml b/modules/control-center-web/pom.xml
new file mode 100644
index 0000000..fcd9b91
--- /dev/null
+++ b/modules/control-center-web/pom.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ /*
+ ~ * 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.
+ ~ */
+ -->
+
+<!--
+ POM file.
+-->
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.ignite</groupId>
+ <artifactId>ignite-parent</artifactId>
+ <version>1</version>
+ <relativePath>../../parent</relativePath>
+ </parent>
+
+ <artifactId>ignite-control-center-web</artifactId>
+ <version>1.5.0-SNAPSHOT</version>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>com.github.eirslett</groupId>
+ <artifactId>frontend-maven-plugin</artifactId>
+ <version>0.0.23</version>
+
+ <configuration>
+ <workingDirectory>src/main/js</workingDirectory>
+ </configuration>
+
+ <executions>
+ <execution>
+ <id>install node and npm</id>
+ <goals>
+ <goal>install-node-and-npm</goal>
+ </goals>
+ <configuration>
+ <nodeVersion>v0.12.7</nodeVersion>
+ <npmVersion>2.13.2</npmVersion>
+ </configuration>
+ </execution>
+
+ <execution>
+ <id>npm install</id>
+ <goals>
+ <goal>npm</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
http://git-wip-us.apache.org/repos/asf/ignite/blob/bce0deb7/modules/control-center-web/src/main/js/.gitignore
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/.gitignore b/modules/control-center-web/src/main/js/.gitignore
new file mode 100644
index 0000000..96db808
--- /dev/null
+++ b/modules/control-center-web/src/main/js/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+*.idea
+*.log
+*.css
http://git-wip-us.apache.org/repos/asf/ignite/blob/bce0deb7/modules/control-center-web/src/main/js/DEVNOTES.txt
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/DEVNOTES.txt b/modules/control-center-web/src/main/js/DEVNOTES.txt
new file mode 100644
index 0000000..4859cf1
--- /dev/null
+++ b/modules/control-center-web/src/main/js/DEVNOTES.txt
@@ -0,0 +1,21 @@
+Ignite Web Console Instructions
+======================================
+
+How to deploy:
+
+1. Install locally NodeJS using installer from site https://nodejs.org for your OS.
+2. Install locally MongoDB follow instructions from site http://docs.mongodb.org/manual/installation
+3. Checkout ignite-843 branch.
+4. Change directory '$IGNITE_HOME/modules/control-center-web/src/main/js'.
+5. Run "npm install" in terminal for download all dependencies.
+
+Steps 1 - 5 should be executed once.
+
+How to run:
+
+1. Run MongoDB.
+ 1.1 In terminal change dir to $MONGO_INSTALL_DIR/server/3.0/bin.
+ 1.2 Run "mongod".
+2. In new terminal change directory '$IGNITE_HOME/modules/control-center-web/src/main/js'.
+3. Start application by executing "npm start".
+4. In browser open: http://localhost:3000
http://git-wip-us.apache.org/repos/asf/ignite/blob/bce0deb7/modules/control-center-web/src/main/js/agents/agent-manager.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/agents/agent-manager.js b/modules/control-center-web/src/main/js/agents/agent-manager.js
new file mode 100644
index 0000000..582cb11
--- /dev/null
+++ b/modules/control-center-web/src/main/js/agents/agent-manager.js
@@ -0,0 +1,312 @@
+/*
+ *
+ * * 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.
+ *
+ */
+
+var WebSocketServer = require('ws').Server;
+
+var apacheIgnite = require('apache-ignite');
+
+var db = require('../db');
+
+var AgentServer = require('./agent-server').AgentServer;
+
+/**
+ * @constructor
+ */
+function AgentManager(srv) {
+ this._clients = {};
+
+ this._server = srv;
+
+ this._wss = new WebSocketServer({ server: this._server });
+
+ var self = this;
+
+ this._wss.on('connection', function(ws) {
+ new Client(ws, self);
+ });
+}
+
+/**
+ * @param userId
+ * @param {Client} client
+ */
+AgentManager.prototype._removeClient = function(userId, client) {
+ var connections = this._clients[userId];
+
+ if (connections) {
+ removeFromArray(connections, client);
+
+ if (connections.length == 0)
+ delete this._clients[userId];
+ }
+};
+
+/**
+ * @param userId
+ * @param {Client} client
+ */
+AgentManager.prototype._addClient = function(userId, client) {
+ var existingConnections = this._clients[userId];
+
+ if (!existingConnections) {
+ existingConnections = [];
+
+ this._clients[userId] = existingConnections;
+ }
+
+ existingConnections.push(client);
+};
+
+/**
+ * @param userId
+ * @return {Client}
+ */
+AgentManager.prototype.findClient = function(userId) {
+ var clientsList = this._clients[userId];
+
+ if (!clientsList || clientsList.length == 0)
+ return null;
+
+ return clientsList[0];
+};
+
+/**
+ * @constructor
+ * @param {AgentManager} manager
+ * @param {WebSocket} ws
+ */
+function Client(ws, manager) {
+ var self = this;
+
+ this._manager = manager;
+ this._ws = ws;
+
+ ws.on('close', function() {
+ if (self._user) {
+ self._manager._removeClient(self._user._id, self);
+ }
+ });
+
+ ws.on('message', function (msgStr) {
+ var msg = JSON.parse(msgStr);
+
+ self['_rmt' + msg.type](msg);
+ });
+
+ this._reqCounter = 0;
+
+ this._cbMap = {};
+}
+
+/**
+ * @param {String} path
+ * @param {Object} params
+ * @param {String} [method]
+ * @param {Object} [headers]
+ * @param {String} [body]
+ * @param {Function} [cb] Callback. Take 3 arguments: {String} error, {number} httpCode, {string} response.
+ */
+Client.prototype.executeRest = function(path, params, method, headers, body, cb) {
+ if (typeof(params) != 'object')
+ throw '"params" argument must be an object';
+
+ if (typeof(cb) != 'function')
+ throw 'callback must be a function';
+
+ if (body && typeof(body) != 'string')
+ throw 'body must be a string';
+
+ if (headers && typeof(headers) != 'object')
+ throw 'headers must be an object';
+
+ if (!method)
+ method = 'GET';
+ else
+ method = method.toUpperCase();
+
+ if (method != 'GET' && method != 'POST')
+ throw 'Unknown HTTP method: ' + method;
+
+ var newArgs = argsToArray(arguments);
+
+ newArgs[5] = function(ex, res) {
+ if (ex)
+ cb(ex.message);
+ else
+ cb(null, res.code, res.message)
+ };
+
+ this._invokeRmtMethod('executeRest', newArgs);
+};
+
+/**
+ * @param {string} error
+ */
+Client.prototype.authResult = function(error) {
+ this._invokeRmtMethod('authResult', arguments)
+};
+
+/**
+ * @param {String} jdbcDriverJarPath
+ * @param {String} jdbcDriverClass
+ * @param {String} jdbcUrl
+ * @param {Object} jdbcInfo
+ * @param {Function} cb Callback. Take two arguments: {Object} exception, {Object} result.
+ * @return {Array} List of tables (see org.apache.ignite.schema.parser.DbTable java class)
+ */
+Client.prototype.metadataSchemas = function(jdbcDriverJarPath, jdbcDriverClass, jdbcUrl, jdbcInfo, cb) {
+ this._invokeRmtMethod('schemas', arguments)
+};
+
+/**
+ * @param {String} jdbcDriverJarPath
+ * @param {String} jdbcDriverClass
+ * @param {String} jdbcUrl
+ * @param {Object} jdbcInfo
+ * @param {Array} schemas
+ * @param {Boolean} tablesOnly
+ * @param {Function} cb Callback. Take two arguments: {Object} exception, {Object} result.
+ * @return {Array} List of tables (see org.apache.ignite.schema.parser.DbTable java class)
+ */
+Client.prototype.metadataTables = function(jdbcDriverJarPath, jdbcDriverClass, jdbcUrl, jdbcInfo, schemas, tablesOnly, cb) {
+ this._invokeRmtMethod('metadata', arguments)
+};
+
+/**
+ * @param {Function} cb Callback. Take two arguments: {Object} exception, {Object} result.
+ * @return {Array} List of jars from driver folder.
+ */
+Client.prototype.availableDrivers = function(cb) {
+ this._invokeRmtMethod('availableDrivers', arguments)
+};
+
+Client.prototype._invokeRmtMethod = function(methodName, args) {
+ var cb = null;
+
+ var m = argsToArray(args);
+
+ if (m.length > 0 && typeof m[m.length - 1] == 'function')
+ cb = m.pop();
+
+ if (this._ws.readyState != 1) {
+ if (cb)
+ cb({type: 'org.apache.ignite.agent.AgentException', message: 'Connection is closed'});
+
+ return
+ }
+
+ var msg = {
+ mtdName: methodName,
+ args: m
+ };
+
+ if (cb) {
+ var reqId = this._reqCounter++;
+
+ this._cbMap[reqId] = cb;
+
+ msg.reqId = reqId;
+ }
+
+ this._ws.send(JSON.stringify(msg))
+};
+
+Client.prototype._rmtAuthMessage = function(msg) {
+ var self = this;
+
+ db.Account.findOne({ token: msg.token }, function (err, account) {
+ if (err) {
+ self.authResult('Failed to authorize user');
+ // TODO IGNITE-1379 send error to web master.
+ }
+ else if (!account)
+ self.authResult('Invalid token, user not found');
+ else {
+ self.authResult(null);
+
+ self._user = account;
+
+ self._manager._addClient(account._id, self);
+
+ self._ignite = new apacheIgnite.Ignite(new AgentServer(self));
+ }
+ });
+};
+
+Client.prototype._rmtCallRes = function(msg) {
+ var cb = this._cbMap[msg.reqId];
+
+ if (!cb) return;
+
+ delete this._cbMap[msg.reqId];
+
+ if (msg.ex)
+ cb(msg.ex);
+ else
+ cb(null, msg.res);
+};
+
+/**
+ * @return {Ignite}
+ */
+Client.prototype.ignite = function() {
+ return this._ignite;
+};
+
+function removeFromArray(arr, val) {
+ var idx;
+
+ while ((idx = arr.indexOf(val)) !== -1) {
+ arr.splice(idx, 1);
+ }
+}
+
+/**
+ * @param args
+ * @returns {Array}
+ */
+function argsToArray(args) {
+ var res = [];
+
+ for (var i = 0; i < args.length; i++)
+ res.push(args[i])
+
+ return res;
+}
+
+exports.AgentManager = AgentManager;
+
+/**
+ * @type {AgentManager}
+ */
+var manager = null;
+
+exports.createManager = function(srv) {
+ if (manager)
+ throw 'Agent manager already cleared!';
+
+ manager = new AgentManager(srv);
+};
+
+/**
+ * @return {AgentManager}
+ */
+exports.getAgentManager = function() {
+ return manager;
+};
http://git-wip-us.apache.org/repos/asf/ignite/blob/bce0deb7/modules/control-center-web/src/main/js/agents/agent-server.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/agents/agent-server.js b/modules/control-center-web/src/main/js/agents/agent-server.js
new file mode 100644
index 0000000..bd7efbf
--- /dev/null
+++ b/modules/control-center-web/src/main/js/agents/agent-server.js
@@ -0,0 +1,98 @@
+/*
+ *
+ * * 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.
+ *
+ */
+
+var _ = require('lodash');
+
+/**
+ * Creates an instance of server for Ignite
+ *
+ * @constructor
+ * @this {AgentServer}
+ * @param {Client} client connected client
+ */
+function AgentServer(client) {
+ this._client = client;
+}
+
+/**
+ * Run http request
+ *
+ * @this {AgentServer}
+ * @param {Command} cmd Command
+ * @param {callback} callback on finish
+ */
+AgentServer.prototype.runCommand = function(cmd, callback) {
+ var params = {cmd: cmd.name()};
+
+ _.forEach(cmd._params, function (p) {
+ params[p.key] = p.value;
+ });
+
+ var body = undefined;
+
+ var headers = undefined;
+
+ var method = 'GET';
+
+ if (cmd._isPost()) {
+ body = cmd.postData();
+
+ method = 'POST';
+
+ headers = {'JSONObject': 'application/json'};
+ }
+
+ this._client.executeRest("ignite", params, method, headers, body, function(error, code, message) {
+ if (error) {
+ callback(error);
+ return
+ }
+
+ if (code !== 200) {
+ if (code === 401) {
+ callback.call(null, "Authentication failed. Status code 401.");
+ }
+ else {
+ callback.call(null, "Request failed. Status code " + code);
+ }
+
+ return;
+ }
+
+ var igniteResponse;
+
+ try {
+ igniteResponse = JSON.parse(message);
+ }
+ catch (e) {
+ callback.call(null, e, null);
+
+ return;
+ }
+
+ if (igniteResponse.successStatus) {
+ callback.call(null, igniteResponse.error, null)
+ }
+ else {
+ callback.call(null, null, igniteResponse.response);
+ }
+ });
+};
+
+exports.AgentServer = AgentServer;
http://git-wip-us.apache.org/repos/asf/ignite/blob/bce0deb7/modules/control-center-web/src/main/js/app.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/app.js b/modules/control-center-web/src/main/js/app.js
new file mode 100644
index 0000000..69f6663
--- /dev/null
+++ b/modules/control-center-web/src/main/js/app.js
@@ -0,0 +1,190 @@
+/*
+ *
+ * * 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.
+ *
+ */
+
+var express = require('express');
+var compress = require('compression');
+var path = require('path');
+var favicon = require('serve-favicon');
+var logger = require('morgan');
+var cookieParser = require('cookie-parser');
+var bodyParser = require('body-parser');
+var session = require('express-session');
+var mongoStore = require('connect-mongo')(session);
+var forceSSL = require('express-force-ssl');
+var config = require('./helpers/configuration-loader.js');
+
+var publicRoutes = require('./routes/public');
+var notebooksRoutes = require('./routes/notebooks');
+var clustersRouter = require('./routes/clusters');
+var cachesRouter = require('./routes/caches');
+var metadataRouter = require('./routes/metadata');
+var presetsRouter = require('./routes/presets');
+var summary = require('./routes/summary');
+var adminRouter = require('./routes/admin');
+var profileRouter = require('./routes/profile');
+var sqlRouter = require('./routes/sql');
+var agentRouter = require('./routes/agent');
+
+var passport = require('passport');
+
+var db = require('./db');
+
+var app = express();
+
+app.use(compress());
+
+app.use(bodyParser.json({limit: '50mb'}));
+app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
+
+// Views engine setup.
+app.set('views', path.join(__dirname, 'views'));
+app.set('view engine', 'jade');
+
+// Site favicon.
+app.use(favicon(__dirname + '/public/favicon.ico'));
+
+app.use(logger('dev'));
+
+app.use(bodyParser.json());
+app.use(bodyParser.urlencoded({extended: false}));
+
+app.use(require('node-sass-middleware')({
+ /* Options */
+ src: path.join(__dirname, 'public'),
+ dest: path.join(__dirname, 'public'),
+ debug: true,
+ outputStyle: 'nested'
+}));
+
+app.use(express.static(path.join(__dirname, 'public')));
+app.use(express.static(path.join(__dirname, 'controllers')));
+app.use(express.static(path.join(__dirname, 'helpers')));
+app.use(express.static(path.join(__dirname, 'routes/generator')));
+
+app.use(cookieParser('keyboard cat'));
+
+app.use(session({
+ secret: 'keyboard cat',
+ resave: false,
+ saveUninitialized: true,
+ store: new mongoStore({
+ mongooseConnection: db.mongoose.connection
+ })
+}));
+
+app.use(passport.initialize());
+app.use(passport.session());
+
+passport.serializeUser(db.Account.serializeUser());
+passport.deserializeUser(db.Account.deserializeUser());
+
+passport.use(db.Account.createStrategy());
+
+if (config.get('server:ssl')) {
+ var httpsPort = config.normalizePort(config.get('server:https-port') || 443);
+
+ app.set('forceSSLOptions', {
+ enable301Redirects: true,
+ trustXFPHeader: true,
+ httpsPort: httpsPort
+ });
+
+ app.use(forceSSL);
+}
+
+var mustAuthenticated = function (req, res, next) {
+ req.isAuthenticated() ? next() : res.redirect('/');
+};
+
+var adminOnly = function(req, res, next) {
+ req.isAuthenticated() && req.user.admin ? next() : res.sendStatus(403);
+};
+
+app.all('/configuration/*', mustAuthenticated);
+
+app.all('*', function(req, res, next) {
+ var becomeUsed = req.session.viewedUser && req.user.admin;
+
+ if (req.url.lastIndexOf('/reset', 0) === 0) {
+ res.locals.user = null;
+ res.locals.becomeUsed = false;
+ }
+ else {
+ res.locals.user = becomeUsed ? req.session.viewedUser : req.user;
+ res.locals.becomeUsed = becomeUsed;
+ }
+
+ req.currentUserId = function() {
+ if (!req.user)
+ return null;
+
+ if (req.session.viewedUser && req.user.admin)
+ return req.session.viewedUser._id;
+
+ return req.user._id;
+ };
+
+ next();
+});
+
+app.use('/', publicRoutes);
+app.use('/admin', mustAuthenticated, adminOnly, adminRouter);
+app.use('/profile', mustAuthenticated, profileRouter);
+
+app.use('/configuration/clusters', clustersRouter);
+app.use('/configuration/caches', cachesRouter);
+app.use('/configuration/metadata', metadataRouter);
+app.use('/configuration/presets', presetsRouter);
+app.use('/configuration/summary', summary);
+
+app.use('/notebooks', mustAuthenticated, notebooksRoutes);
+app.use('/sql', mustAuthenticated, sqlRouter);
+
+app.use('/agent', mustAuthenticated, agentRouter);
+
+// Catch 404 and forward to error handler.
+app.use(function (req, res, next) {
+ var err = new Error('Not Found: ' + req.originalUrl);
+ err.status = 404;
+ next(err);
+});
+
+// Error handlers.
+
+// Development error handler: will print stacktrace.
+if (app.get('env') === 'development') {
+ app.use(function (err, req, res) {
+ res.status(err.status || 500);
+ res.render('error', {
+ message: err.message,
+ error: err
+ });
+ });
+}
+
+// Production error handler: no stacktraces leaked to user.
+app.use(function (err, req, res) {
+ res.status(err.status || 500);
+ res.render('error', {
+ message: err.message,
+ error: {}
+ });
+});
+
+module.exports = app;
http://git-wip-us.apache.org/repos/asf/ignite/blob/bce0deb7/modules/control-center-web/src/main/js/bin/www
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/bin/www b/modules/control-center-web/src/main/js/bin/www
new file mode 100644
index 0000000..69e73e3
--- /dev/null
+++ b/modules/control-center-web/src/main/js/bin/www
@@ -0,0 +1,126 @@
+/*
+ *
+ * * 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.
+ *
+ */
+
+#!/usr/bin/env node
+
+/**
+ * Module dependencies.
+ */
+var http = require('http');
+var https = require('https');
+var config = require('../helpers/configuration-loader.js');
+var app = require('../app');
+var agentManager = require('../agents/agent-manager');
+
+var fs = require('fs');
+
+var debug = require('debug')('ignite-web-console:server');
+
+/**
+ * Get port from environment and store in Express.
+ */
+var port = config.normalizePort(config.get('server:port') || process.env.PORT || 80);
+
+// Create HTTP server.
+var server = http.createServer(app);
+
+app.set('port', port);
+
+/**
+ * Listen on provided port, on all network interfaces.
+ */
+server.listen(port);
+server.on('error', onError);
+server.on('listening', onListening);
+
+if (config.get('server:ssl')) {
+ httpsServer = https.createServer({
+ key: fs.readFileSync(config.get('server:key')),
+ cert: fs.readFileSync(config.get('server:cert')),
+ passphrase: config.get('server:keyPassphrase')
+ }, app);
+
+ var httpsPort = config.normalizePort(config.get('server:https-port') || 443);
+
+ /**
+ * Listen on provided port, on all network interfaces.
+ */
+ httpsServer.listen(httpsPort);
+ httpsServer.on('error', onError);
+ httpsServer.on('listening', onListening);
+}
+
+/**
+ * Start agent server.
+ */
+var agentServer;
+
+if (config.get('agent-server:ssl')) {
+ agentServer = https.createServer({
+ key: fs.readFileSync(config.get('agent-server:key')),
+ cert: fs.readFileSync(config.get('agent-server:cert')),
+ passphrase: config.get('agent-server:keyPassphrase')
+ });
+}
+else {
+ agentServer = http.createServer();
+}
+
+agentServer.listen(config.get('agent-server:port'));
+
+agentManager.createManager(agentServer);
+
+/**
+ * Event listener for HTTP server "error" event.
+ */
+function onError(error) {
+ if (error.syscall !== 'listen') {
+ throw error;
+ }
+
+ var bind = typeof port === 'string'
+ ? 'Pipe ' + port
+ : 'Port ' + port;
+
+ // handle specific listen errors with friendly messages
+ switch (error.code) {
+ case 'EACCES':
+ console.error(bind + ' requires elevated privileges');
+ process.exit(1);
+ break;
+ case 'EADDRINUSE':
+ console.error(bind + ' is already in use');
+ process.exit(1);
+ break;
+ default:
+ throw error;
+ }
+}
+
+/**
+ * Event listener for HTTP server "listening" event.
+ */
+function onListening() {
+ var addr = server.address();
+ var bind = typeof addr === 'string'
+ ? 'pipe ' + addr
+ : 'port ' + addr.port;
+
+ debug('Listening on ' + bind);
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/bce0deb7/modules/control-center-web/src/main/js/config/default.json
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/config/default.json b/modules/control-center-web/src/main/js/config/default.json
new file mode 100644
index 0000000..bf1e88b
--- /dev/null
+++ b/modules/control-center-web/src/main/js/config/default.json
@@ -0,0 +1,25 @@
+{
+ "server": {
+ "port": 3000,
+ "https-port": 8443,
+ "ssl": false,
+ "key": "keys/test.key",
+ "cert": "keys/test.crt",
+ "keyPassphrase": "password"
+ },
+ "mongoDB": {
+ "url": "mongodb://localhost/web-control-center"
+ },
+ "agent-server": {
+ "port": 3001,
+ "ssl": true,
+ "key": "keys/test.key",
+ "cert": "keys/test.crt",
+ "keyPassphrase": "password"
+ },
+ "smtp": {
+ "service": "",
+ "username": "",
+ "password": ""
+ }
+}
http://git-wip-us.apache.org/repos/asf/ignite/blob/bce0deb7/modules/control-center-web/src/main/js/controllers/admin-controller.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/admin-controller.js b/modules/control-center-web/src/main/js/controllers/admin-controller.js
new file mode 100644
index 0000000..0b57da5
--- /dev/null
+++ b/modules/control-center-web/src/main/js/controllers/admin-controller.js
@@ -0,0 +1,81 @@
+/*
+ *
+ * * 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.
+ *
+ */
+
+// Controller for Admin screen.
+consoleModule.controller('adminController',
+ ['$scope', '$window', '$http', '$common', '$confirm',
+ function ($scope, $window, $http, $common, $confirm) {
+ $scope.users = null;
+
+ function reload() {
+ $http.post('admin/list')
+ .success(function (data) {
+ $scope.users = data;
+ })
+ .error(function (errMsg) {
+ $common.showError($common.errorMessage(errMsg));
+ });
+ }
+
+ reload();
+
+ $scope.becomeUser = function (user) {
+ $window.location = '/admin/become?viewedUserId=' + user._id;
+ };
+
+ $scope.removeUser = function (user) {
+ $confirm.confirm('Are you sure you want to remove user: "' + user.username + '"?')
+ .then(function () {
+ $http.post('admin/remove', {userId: user._id}).success(
+ function () {
+ var i = _.findIndex($scope.users, function (u) {
+ return u._id == user._id;
+ });
+
+ if (i >= 0)
+ $scope.users.splice(i, 1);
+
+ $common.showInfo('User has been removed: "' + user.username + '"');
+ }).error(function (errMsg, status) {
+ if (status == 503)
+ $common.showInfo(errMsg);
+ else
+ $common.showError('Failed to remove user: "' + $common.errorMessage(errMsg) + '"');
+ });
+ });
+ };
+
+ $scope.toggleAdmin = function (user) {
+ if (user.adminChanging)
+ return;
+
+ user.adminChanging = true;
+
+ $http.post('admin/save', {userId: user._id, adminFlag: user.admin}).success(
+ function () {
+ $common.showInfo('Admin right was successfully toggled for user: "' + user.username + '"');
+
+ user.adminChanging = false;
+ }).error(function (errMsg) {
+ $common.showError('Failed to toggle admin right for user: "' + $common.errorMessage(errMsg) + '"');
+
+ user.adminChanging = false;
+ });
+ }
+}]);
http://git-wip-us.apache.org/repos/asf/ignite/blob/bce0deb7/modules/control-center-web/src/main/js/controllers/caches-controller.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/caches-controller.js b/modules/control-center-web/src/main/js/controllers/caches-controller.js
new file mode 100644
index 0000000..28cedf0
--- /dev/null
+++ b/modules/control-center-web/src/main/js/controllers/caches-controller.js
@@ -0,0 +1,594 @@
+/*
+ *
+ * * 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.
+ *
+ */
+
+// Controller for Caches screen.
+consoleModule.controller('cachesController', [
+ '$scope', '$controller', '$filter', '$http', '$timeout', '$common', '$focus', '$confirm', '$message', '$clone', '$table', '$preview', '$loading', '$unsavedChangesGuard',
+ function ($scope, $controller, $filter, $http, $timeout, $common, $focus, $confirm, $message, $clone, $table, $preview, $loading, $unsavedChangesGuard) {
+ $unsavedChangesGuard.install($scope);
+
+ // Initialize the super class and extend it.
+ angular.extend(this, $controller('save-remove', {$scope: $scope}));
+
+ $scope.ui = $common.formUI();
+
+ $scope.showMoreInfo = $message.message;
+
+ $scope.joinTip = $common.joinTip;
+ $scope.getModel = $common.getModel;
+ $scope.javaBuildInClasses = $common.javaBuildInClasses;
+ $scope.compactJavaName = $common.compactJavaName;
+ $scope.saveBtnTipText = $common.saveBtnTipText;
+
+ $scope.tableReset = $table.tableReset;
+ $scope.tableNewItem = $table.tableNewItem;
+ $scope.tableNewItemActive = $table.tableNewItemActive;
+ $scope.tableEditing = $table.tableEditing;
+ $scope.tableStartEdit = $table.tableStartEdit;
+ $scope.tableRemove = function (item, field, index) {
+ $table.tableRemove(item, field, index);
+ };
+
+ $scope.tableSimpleSave = $table.tableSimpleSave;
+ $scope.tableSimpleSaveVisible = $table.tableSimpleSaveVisible;
+ $scope.tableSimpleUp = $table.tableSimpleUp;
+ $scope.tableSimpleDown = $table.tableSimpleDown;
+ $scope.tableSimpleDownVisible = $table.tableSimpleDownVisible;
+
+ $scope.tablePairSave = $table.tablePairSave;
+ $scope.tablePairSaveVisible = $table.tablePairSaveVisible;
+
+ var previews = [];
+
+ $scope.previewInit = function (preview) {
+ previews.push(preview);
+
+ $preview.previewInit(preview);
+ };
+
+ $scope.previewChanged = $preview.previewChanged;
+
+ $scope.hidePopover = $common.hidePopover;
+
+ var showPopoverMessage = $common.showPopoverMessage;
+
+ $scope.atomicities = $common.mkOptions(['ATOMIC', 'TRANSACTIONAL']);
+
+ $scope.cacheModes = $common.mkOptions(['PARTITIONED', 'REPLICATED', 'LOCAL']);
+
+ $scope.atomicWriteOrderModes = $common.mkOptions(['CLOCK', 'PRIMARY']);
+
+ $scope.memoryModes = $common.mkOptions(['ONHEAP_TIERED', 'OFFHEAP_TIERED', 'OFFHEAP_VALUES']);
+
+ $scope.evictionPolicies = [
+ {value: 'LRU', label: 'LRU'},
+ {value: 'RND', label: 'Random'},
+ {value: 'FIFO', label: 'FIFO'},
+ {value: 'SORTED', label: 'Sorted'},
+ {value: undefined, label: 'Not set'}
+ ];
+
+ $scope.rebalanceModes = $common.mkOptions(['SYNC', 'ASYNC', 'NONE']);
+
+ $scope.cacheStoreFactories = [
+ {value: 'CacheJdbcPojoStoreFactory', label: 'JDBC POJO store factory'},
+ {value: 'CacheJdbcBlobStoreFactory', label: 'JDBC BLOB store factory'},
+ {value: 'CacheHibernateBlobStoreFactory', label: 'Hibernate BLOB store factory'},
+ {value: undefined, label: 'Not set'}
+ ];
+
+ $scope.cacheStoreJdbcDialects = [
+ {value: 'Oracle', label: 'Oracle'},
+ {value: 'DB2', label: 'IBM DB2'},
+ {value: 'SQLServer', label: 'Microsoft SQL Server'},
+ {value: 'MySQL', label: 'My SQL'},
+ {value: 'PostgreSQL', label: 'Postgre SQL'},
+ {value: 'H2', label: 'H2 database'}
+ ];
+
+ $scope.toggleExpanded = function () {
+ $scope.ui.expanded = !$scope.ui.expanded;
+
+ $common.hidePopover();
+ };
+
+ $scope.panels = {activePanels: [0]};
+
+ $scope.general = [];
+ $scope.advanced = [];
+ $scope.caches = [];
+ $scope.metadatas = [];
+
+ $scope.preview = {
+ general: {xml: '', java: '', allDefaults: true},
+ memory: {xml: '', java: '', allDefaults: true},
+ query: {xml: '', java: '', allDefaults: true},
+ store: {xml: '', java: '', allDefaults: true},
+ concurrency: {xml: '', java: '', allDefaults: true},
+ rebalance: {xml: '', java: '', allDefaults: true},
+ serverNearCache: {xml: '', java: '', allDefaults: true},
+ statistics: {xml: '', java: '', allDefaults: true}
+ };
+
+ $scope.required = function (field) {
+ var model = $common.isDefined(field.path) ? field.path + '.' + field.model : field.model;
+
+ var backupItem = $scope.backupItem;
+
+ var memoryMode = backupItem.memoryMode;
+
+ var onHeapTired = memoryMode == 'ONHEAP_TIERED';
+ var offHeapTired = memoryMode == 'OFFHEAP_TIERED';
+
+ var offHeapMaxMemory = backupItem.offHeapMaxMemory;
+
+ if (model == 'offHeapMaxMemory' && offHeapTired)
+ return true;
+
+ if (model == 'evictionPolicy.kind' && onHeapTired)
+ return backupItem.swapEnabled || ($common.isDefined(offHeapMaxMemory) && offHeapMaxMemory >= 0);
+
+ return false;
+ };
+
+ $scope.tableSimpleValid = function (item, field, fx, index) {
+ var model;
+
+ switch (field.model) {
+ case 'hibernateProperties':
+ if (fx.indexOf('=') < 0)
+ return showPopoverMessage(null, null, $table.tableFieldId(index, 'HibProp'), 'Property should be present in format key=value!');
+
+ model = item.cacheStoreFactory.CacheHibernateBlobStoreFactory[field.model];
+
+ var key = fx.split('=')[0];
+
+ var exist = false;
+
+ if ($common.isDefined(model)) {
+ model.forEach(function (val) {
+ if (val.split('=')[0] == key)
+ exist = true;
+ })
+ }
+
+ if (exist)
+ return showPopoverMessage(null, null, $table.tableFieldId(index, 'HibProp'), 'Property with such name already exists!');
+
+ break;
+
+ case 'sqlFunctionClasses':
+ if (!$common.isValidJavaClass('SQL function', fx, false, $table.tableFieldId(index, 'SqlFx')))
+ return $table.tableFocusInvalidField(index, 'SqlFx');
+
+ model = item[field.model];
+
+ if ($common.isDefined(model)) {
+ var idx = _.indexOf(model, fx);
+
+ // Found duplicate.
+ if (idx >= 0 && idx != index)
+ return showPopoverMessage(null, null, $table.tableFieldId(index, 'SqlFx'), 'SQL function with such class name already exists!');
+ }
+ }
+
+ return true;
+ };
+
+ $scope.tablePairValid = function (item, field, index) {
+ var pairValue = $table.tablePairValue(field, index);
+
+ if (!$common.isValidJavaClass('Indexed type key', pairValue.key, true, $table.tableFieldId(index, 'KeyIndexedType')))
+ return $table.tableFocusInvalidField(index, 'KeyIndexedType');
+
+ if (!$common.isValidJavaClass('Indexed type value', pairValue.value, true, $table.tableFieldId(index, 'ValueIndexedType')))
+ return $table.tableFocusInvalidField(index, 'ValueIndexedType');
+
+ var model = item[field.model];
+
+ if ($common.isDefined(model)) {
+ var idx = _.findIndex(model, function (pair) {
+ return pair.keyClass == pairValue.key && pair.valueClass == pairValue.value;
+ });
+
+ // Found duplicate.
+ if (idx >= 0 && idx != index)
+ return showPopoverMessage(null, null, $table.tableFieldId(index, 'ValueIndexedType'), 'Indexed type with such key and value classes already exists!');
+ }
+
+ return true;
+ };
+
+ function selectFirstItem() {
+ if ($scope.caches.length > 0)
+ $scope.selectItem($scope.caches[0]);
+ }
+
+ function cacheMetadatas(item) {
+ return _.reduce($scope.metadatas, function (memo, meta) {
+ if (item && _.contains(item.metadatas, meta.value)) {
+ memo.push(meta.meta);
+ }
+
+ return memo;
+ }, []);
+ }
+
+ $loading.start('loadingCachesScreen');
+
+ // When landing on the page, get caches and show them.
+ $http.post('caches/list')
+ .success(function (data) {
+ var validFilter = $filter('metadatasValidation');
+
+ $scope.spaces = data.spaces;
+ $scope.caches = data.caches;
+ $scope.clusters = data.clusters;
+ $scope.metadatas = _.sortBy(_.map(validFilter(data.metadatas, true, false), function (meta) {
+ return {value: meta._id, label: meta.valueType, kind: meta.kind, meta: meta}
+ }), 'label');
+
+ // Load page descriptor.
+ $http.get('/models/caches.json')
+ .success(function (data) {
+ $scope.screenTip = data.screenTip;
+ $scope.moreInfo = data.moreInfo;
+ $scope.general = data.general;
+ $scope.advanced = data.advanced;
+
+ $scope.ui.addGroups(data.general, data.advanced);
+
+ if ($common.getQueryVariable('new'))
+ $scope.createItem();
+ else {
+ var lastSelectedCache = angular.fromJson(sessionStorage.lastSelectedCache);
+
+ if (lastSelectedCache) {
+ var idx = _.findIndex($scope.caches, function (cache) {
+ return cache._id == lastSelectedCache;
+ });
+
+ if (idx >= 0)
+ $scope.selectItem($scope.caches[idx]);
+ else {
+ sessionStorage.removeItem('lastSelectedCache');
+
+ selectFirstItem();
+ }
+
+ }
+ else
+ selectFirstItem();
+ }
+
+ $scope.$watch('backupItem', function (val) {
+ if (val) {
+ var srcItem = $scope.selectedItem ? $scope.selectedItem : prepareNewItem();
+
+ $scope.ui.checkDirty(val, srcItem);
+
+ var metas = cacheMetadatas(val);
+ var varName = $commonUtils.toJavaName('cache', val.name);
+
+ $scope.preview.general.xml = $generatorXml.cacheMetadatas(metas, $generatorXml.cacheGeneral(val)).asString();
+ $scope.preview.general.java = $generatorJava.cacheMetadatas(metas, varName, $generatorJava.cacheGeneral(val, varName)).asString();
+ $scope.preview.general.allDefaults = $common.isEmptyString($scope.preview.general.xml);
+
+ $scope.preview.memory.xml = $generatorXml.cacheMemory(val).asString();
+ $scope.preview.memory.java = $generatorJava.cacheMemory(val, varName).asString();
+ $scope.preview.memory.allDefaults = $common.isEmptyString($scope.preview.memory.xml);
+
+ $scope.preview.query.xml = $generatorXml.cacheQuery(val).asString();
+ $scope.preview.query.java = $generatorJava.cacheQuery(val, varName).asString();
+ $scope.preview.query.allDefaults = $common.isEmptyString($scope.preview.query.xml);
+
+ $scope.preview.store.xml = $generatorXml.cacheStore(val).asString();
+ $scope.preview.store.java = $generatorJava.cacheStore(val, varName).asString();
+ $scope.preview.store.allDefaults = $common.isEmptyString($scope.preview.store.xml);
+
+ $scope.preview.concurrency.xml = $generatorXml.cacheConcurrency(val).asString();
+ $scope.preview.concurrency.java = $generatorJava.cacheConcurrency(val, varName).asString();
+ $scope.preview.concurrency.allDefaults = $common.isEmptyString($scope.preview.concurrency.xml);
+
+ $scope.preview.rebalance.xml = $generatorXml.cacheRebalance(val).asString();
+ $scope.preview.rebalance.java = $generatorJava.cacheRebalance(val, varName).asString();
+ $scope.preview.rebalance.allDefaults = $common.isEmptyString($scope.preview.rebalance.xml);
+
+ $scope.preview.serverNearCache.xml = $generatorXml.cacheServerNearCache(val).asString();
+ $scope.preview.serverNearCache.java = $generatorJava.cacheServerNearCache(val, varName).asString();
+ $scope.preview.serverNearCache.allDefaults = $common.isEmptyString($scope.preview.serverNearCache.xml);
+
+ $scope.preview.statistics.xml = $generatorXml.cacheStatistics(val).asString();
+ $scope.preview.statistics.java = $generatorJava.cacheStatistics(val, varName).asString();
+ $scope.preview.statistics.allDefaults = $common.isEmptyString($scope.preview.statistics.xml);
+ }
+ }, true);
+
+ $scope.$watch('backupItem.metadatas', function (val) {
+ var item = $scope.backupItem;
+
+ var cacheStoreFactory = $common.isDefined(item) &&
+ $common.isDefined(item.cacheStoreFactory) &&
+ $common.isDefined(item.cacheStoreFactory.kind);
+
+ if (val && !cacheStoreFactory) {
+ if (_.findIndex(cacheMetadatas(item), $common.metadataForStoreConfigured) >= 0) {
+ item.cacheStoreFactory.kind = 'CacheJdbcPojoStoreFactory';
+
+ if (!item.readThrough && !item.writeThrough) {
+ item.readThrough = true;
+ item.writeThrough = true;
+ }
+
+ $timeout(function () {
+ $common.ensureActivePanel($scope.panels, 'store');
+ });
+ }
+ }
+ }, true);
+ })
+ .error(function (errMsg) {
+ $common.showError(errMsg);
+ });
+ })
+ .error(function (errMsg) {
+ $common.showError(errMsg);
+ })
+ .finally(function () {
+ $scope.ui.ready = true;
+ $loading.finish('loadingCachesScreen');
+ });
+
+ $scope.selectItem = function (item, backup) {
+ function selectItem() {
+ $table.tableReset();
+
+ $scope.selectedItem = angular.copy(item);
+
+ try {
+ if (item)
+ sessionStorage.lastSelectedCache = angular.toJson(item._id);
+ else
+ sessionStorage.removeItem('lastSelectedCache');
+ }
+ catch (error) { }
+
+ _.forEach(previews, function(preview) {
+ preview.attractAttention = false;
+ });
+
+ if (backup)
+ $scope.backupItem = backup;
+ else if (item)
+ $scope.backupItem = angular.copy(item);
+ else
+ $scope.backupItem = undefined;
+ }
+
+ $common.confirmUnsavedChanges($scope.ui.isDirty(), selectItem);
+
+ $scope.ui.formTitle = $common.isDefined($scope.backupItem) && $scope.backupItem._id ?
+ 'Selected cache: ' + $scope.backupItem.name : 'New cache';
+ };
+
+ function prepareNewItem() {
+ return {
+ space: $scope.spaces[0]._id,
+ cacheMode: 'PARTITIONED',
+ atomicityMode: 'ATOMIC',
+ readFromBackup: true,
+ copyOnRead: true,
+ clusters: [],
+ metadatas: []
+ }
+ }
+
+ // Add new cache.
+ $scope.createItem = function () {
+ $table.tableReset();
+
+ $timeout(function () {
+ $common.ensureActivePanel($scope.panels, 'general', 'cacheName');
+ });
+
+ $scope.selectItem(undefined, prepareNewItem());
+ };
+
+ // Check cache logical consistency.
+ function validate(item) {
+ if ($common.isEmptyString(item.name))
+ return showPopoverMessage($scope.panels, 'general', 'cacheName', 'Name should not be empty');
+
+ if (item.memoryMode == 'OFFHEAP_TIERED' && item.offHeapMaxMemory == null)
+ return showPopoverMessage($scope.panels, 'memory', 'offHeapMaxMemory',
+ 'Off-heap max memory should be specified');
+
+ if (item.memoryMode == 'ONHEAP_TIERED' && item.offHeapMaxMemory > 0 &&
+ !$common.isDefined(item.evictionPolicy.kind)) {
+ return showPopoverMessage($scope.panels, 'memory', 'evictionPolicy', 'Eviction policy should not be configured');
+ }
+
+ var cacheStoreFactorySelected = item.cacheStoreFactory && item.cacheStoreFactory.kind;
+
+ if (cacheStoreFactorySelected) {
+ if (item.cacheStoreFactory.kind == 'CacheJdbcPojoStoreFactory') {
+ if ($common.isEmptyString(item.cacheStoreFactory.CacheJdbcPojoStoreFactory.dataSourceBean))
+ return showPopoverMessage($scope.panels, 'store', 'dataSourceBean',
+ 'Data source bean should not be empty');
+
+ if (!item.cacheStoreFactory.CacheJdbcPojoStoreFactory.dialect)
+ return showPopoverMessage($scope.panels, 'store', 'dialect',
+ 'Dialect should not be empty');
+ }
+
+ if (item.cacheStoreFactory.kind == 'CacheJdbcBlobStoreFactory') {
+ if ($common.isEmptyString(item.cacheStoreFactory.CacheJdbcBlobStoreFactory.user))
+ return showPopoverMessage($scope.panels, 'store', 'user',
+ 'User should not be empty');
+
+ if ($common.isEmptyString(item.cacheStoreFactory.CacheJdbcBlobStoreFactory.dataSourceBean))
+ return showPopoverMessage($scope.panels, 'store', 'dataSourceBean',
+ 'Data source bean should not be empty');
+ }
+ }
+
+ if ((item.readThrough || item.writeThrough) && !cacheStoreFactorySelected)
+ return showPopoverMessage($scope.panels, 'store', 'cacheStoreFactory',
+ (item.readThrough ? 'Read' : 'Write') + ' through are enabled but store is not configured!');
+
+ if (item.writeBehindEnabled && !cacheStoreFactorySelected)
+ return showPopoverMessage($scope.panels, 'store', 'cacheStoreFactory',
+ 'Write behind enabled but store is not configured!');
+
+ if (cacheStoreFactorySelected) {
+ if (!item.readThrough && !item.writeThrough)
+ return showPopoverMessage($scope.panels, 'store', 'readThrough',
+ 'Store is configured but read/write through are not enabled!');
+
+ if (item.cacheStoreFactory.kind == 'CacheJdbcPojoStoreFactory') {
+ if ($common.isDefined(item.metadatas)) {
+ var metadatas = cacheMetadatas($scope.backupItem);
+
+ if (_.findIndex(metadatas, $common.metadataForStoreConfigured) < 0)
+ return showPopoverMessage($scope.panels, 'general', 'metadata',
+ 'Cache with configured JDBC POJO store factory should contain at least one metadata with store configuration');
+ }
+ }
+ }
+
+ return true;
+ }
+
+ // Save cache into database.
+ function save(item) {
+ $http.post('caches/save', item)
+ .success(function (_id) {
+ $scope.ui.markPristine();
+
+ var idx = _.findIndex($scope.caches, function (cache) {
+ return cache._id == _id;
+ });
+
+ if (idx >= 0)
+ angular.extend($scope.caches[idx], item);
+ else {
+ item._id = _id;
+ $scope.caches.push(item);
+ }
+
+ $scope.selectItem(item);
+
+ $common.showInfo('Cache "' + item.name + '" saved.');
+ })
+ .error(function (errMsg) {
+ $common.showError(errMsg);
+ });
+ }
+
+ // Save cache.
+ $scope.saveItem = function () {
+ $table.tableReset();
+
+ var item = $scope.backupItem;
+
+ if (validate(item))
+ save(item);
+ };
+
+ // Save cache with new name.
+ $scope.cloneItem = function () {
+ $table.tableReset();
+
+ if (validate($scope.backupItem))
+ $clone.confirm($scope.backupItem.name).then(function (newName) {
+ var item = angular.copy($scope.backupItem);
+
+ item._id = undefined;
+ item.name = newName;
+
+ save(item);
+ });
+ };
+
+ // Remove cache from db.
+ $scope.removeItem = function () {
+ $table.tableReset();
+
+ var selectedItem = $scope.selectedItem;
+
+ $confirm.confirm('Are you sure you want to remove cache: "' + selectedItem.name + '"?')
+ .then(function () {
+ var _id = selectedItem._id;
+
+ $http.post('caches/remove', {_id: _id})
+ .success(function () {
+ $common.showInfo('Cache has been removed: ' + selectedItem.name);
+
+ var caches = $scope.caches;
+
+ var idx = _.findIndex(caches, function (cache) {
+ return cache._id == _id;
+ });
+
+ if (idx >= 0) {
+ caches.splice(idx, 1);
+
+ if (caches.length > 0)
+ $scope.selectItem(caches[0]);
+ else
+ $scope.selectItem(undefined, undefined);
+ }
+ })
+ .error(function (errMsg) {
+ $common.showError(errMsg);
+ });
+ });
+ };
+
+ // Remove all caches from db.
+ $scope.removeAllItems = function () {
+ $table.tableReset();
+
+ $confirm.confirm('Are you sure you want to remove all caches?')
+ .then(function () {
+ $http.post('caches/remove/all')
+ .success(function () {
+ $common.showInfo('All caches have been removed');
+
+ $scope.caches = [];
+
+ $scope.selectItem(undefined, undefined);
+ })
+ .error(function (errMsg) {
+ $common.showError(errMsg);
+ });
+ });
+ };
+
+ $scope.resetItem = function (group) {
+ var resetTo = $scope.selectedItem;
+
+ if (!$common.isDefined(resetTo))
+ resetTo = prepareNewItem();
+
+ $common.resetItem($scope.backupItem, resetTo, $scope.general, group);
+ $common.resetItem($scope.backupItem, resetTo, $scope.advanced, group);
+ }
+ }]
+);