ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From anovi...@apache.org
Subject [28/28] incubator-ignite git commit: # ignite-1155_1 Review.
Date Thu, 30 Jul 2015 09:37:12 GMT
# ignite-1155_1 Review.


Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/3017ad52
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/3017ad52
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/3017ad52

Branch: refs/heads/ignite-1155_1
Commit: 3017ad52505c8ef9ec1048fe735a053d41a9720d
Parents: 1c6280e
Author: Andrey <anovikov@gridgain.com>
Authored: Thu Jul 30 16:37:45 2015 +0700
Committer: Andrey <anovikov@gridgain.com>
Committed: Thu Jul 30 16:37:45 2015 +0700

----------------------------------------------------------------------
 .../apache/ignite/agent/AgentCommandLine.java   |   7 +-
 .../apache/ignite/agent/AgentConfiguration.java |   6 +-
 .../org/apache/ignite/agent/AgentSocket.java    |   8 +-
 .../ignite/agent/remote/RemoteCallable.java     |   4 +-
 .../src/main/js/agents/agent-manager.js         | 309 +++++++++++++++++++
 .../src/main/js/agents/agent-server.js          |  94 ++++++
 .../src/main/js/keys/test.crt                   |  13 +
 .../src/main/js/keys/test.key                   |  18 ++
 .../src/main/js/routes/agent.js                 |  37 +++
 .../src/main/js/agents/agent-manager.js         | 309 -------------------
 .../src/main/js/agents/agent-server.js          |  94 ------
 .../src/main/js/keys/test.crt                   |  13 -
 .../src/main/js/keys/test.key                   |  18 --
 .../src/main/js/routes/agent.js                 |  37 ---
 .../src/main/js/views/sql.jade                  |  70 -----
 .../src/test/js/routes/agent.js                 |  94 ------
 16 files changed, 485 insertions(+), 646 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3017ad52/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentCommandLine.java
----------------------------------------------------------------------
diff --git a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentCommandLine.java
b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentCommandLine.java
index 59661f7..b9dfb76 100644
--- a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentCommandLine.java
+++ b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentCommandLine.java
@@ -1,4 +1,4 @@
-package org.apache.ignite.agent;/*
+/*
  * 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.
@@ -15,6 +15,8 @@ package org.apache.ignite.agent;/*
  * limitations under the License.
  */
 
+package org.apache.ignite.agent;
+
 import com.beust.jcommander.*;
 
 /**
@@ -30,7 +32,8 @@ public class AgentCommandLine {
     private String pwd;
 
     /** */
-    @Parameter(names = {"-s", "--serverUri"}, description = "Link to web-control-center web-socket
server, for example: wss://control-center.gridgain.com")
+    @Parameter(names = {"-s", "--serverUri"},
+        description = "Link to web-control-center web-socket server, for example: wss://control-center.gridgain.com")
     private String serverUri;
 
     /** */

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3017ad52/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentConfiguration.java
b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentConfiguration.java
index 852367a..9f01983 100644
--- a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentConfiguration.java
+++ b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentConfiguration.java
@@ -73,7 +73,7 @@ public class AgentConfiguration {
     }
 
     /**
-     * @param srvUri Uri.
+     * @param srvUri URI.
      */
     public void setServerUri(URI srvUri) {
         this.serverUri = srvUri;
@@ -87,14 +87,14 @@ public class AgentConfiguration {
     }
 
     /**
-     * @param nodeUri Node uri.
+     * @param nodeUri Node URI.
      */
     public void setNodeUri(URI nodeUri) {
         this.nodeUri = nodeUri;
     }
 
     /**
-     * @param cfgUrl Url.
+     * @param cfgUrl URL.
      */
     public void load(URL cfgUrl) throws IOException {
         Properties props = new Properties();

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3017ad52/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentSocket.java
----------------------------------------------------------------------
diff --git a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentSocket.java
b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentSocket.java
index 8d6999c..95e499e 100644
--- a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentSocket.java
+++ b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentSocket.java
@@ -99,7 +99,7 @@ public class AgentSocket implements WebSocketSender {
      * @param msg Message.
      * @return Whether or not message was sent.
      */
-    public boolean send(JsonObject msg) {
+    @Override public boolean send(JsonObject msg) {
         return send(Utils.GSON.toJson(msg));
     }
 
@@ -107,13 +107,13 @@ public class AgentSocket implements WebSocketSender {
      * @param msg Message.
      * @return Whether or not message was sent.
      */
-    public boolean send(String msg) {
+    @Override public boolean send(String msg) {
         try {
             ses.getRemote().sendString(msg);
 
             return true;
         }
-        catch (IOException e) {
+        catch (IOException ignored) {
             log.log(Level.SEVERE, "Failed to send message to Control Center");
 
             return false;
@@ -148,7 +148,7 @@ public class AgentSocket implements WebSocketSender {
     }
 
     /**
-     * @param errorMsg Authentication failed message or {@code null} if authentication succes.
+     * @param errorMsg Authentication failed message or {@code null} if authentication success.
      */
     @Remote
     public void authResult(String errorMsg) {

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3017ad52/modules/control-center-agent/src/main/java/org/apache/ignite/agent/remote/RemoteCallable.java
----------------------------------------------------------------------
diff --git a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/remote/RemoteCallable.java
b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/remote/RemoteCallable.java
index 84dbc79..6f1fb19 100644
--- a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/remote/RemoteCallable.java
+++ b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/remote/RemoteCallable.java
@@ -204,7 +204,7 @@ public class RemoteCallable implements AutoCloseable {
     }
 
     /** {@inheritDoc} */
-    public void close() {
+    @Override public void close() {
         executorSrvc.shutdown();
     }
 
@@ -237,7 +237,7 @@ public class RemoteCallable implements AutoCloseable {
          * @param hnd Handler.
          * @param async Async.
          */
-        public MethodDescriptor(Method mtd, Object hnd, boolean async) {
+        MethodDescriptor(Method mtd, Object hnd, boolean async) {
             this.mtd = mtd;
             this.hnd = hnd;
             this.async = async;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3017ad52/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..423165e
--- /dev/null
+++ b/modules/control-center-web/src/main/js/agents/agent-manager.js
@@ -0,0 +1,309 @@
+/*
+ * 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 ignite = 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) {
+        var client = 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)
+        return null;
+
+    return clientsList[0];
+};
+
+/**
+ * For tests only!!!
+ * @return {Client}
+ */
+AgentManager.prototype._getFirstClient = function() {
+    for (var userId in this._clients) {
+        if (this._clients.hasOwnProperty(userId)) {
+            var m = this._clients[userId];
+
+            if (m.length > 0)
+                return m[0];
+        }
+    }
+
+    return null;
+};
+
+
+/**
+ * @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) {
+    var self = this;
+
+    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(err, ex, res) {
+        if (err)
+            cb(err);
+        else 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 {Boolean} tablesOnly
+ * @param {Function} cb Callback. Take 3 arguments: {String} error, {Object} exception, {Object}
result.
+ */
+Client.prototype.extractMetadata = function(jdbcDriverJarPath, jdbcDriverClass, jdbcUrl,
jdbcInfo, tablesOnly, cb) {
+    this._invokeRmtMethod('extractMetadata', 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();
+
+    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;
+
+    var account = db.Account.findByUsername(msg.login, function(err, account) {
+        if (err) {
+            self.authResult("User not found");
+        }
+        else {
+            account.authenticate(msg.password, function(err, user, res) {
+                if (!user) {
+                    self.authResult(res.message);
+                }
+                else {
+                    self.authResult(null);
+
+                    self._user = account;
+
+                    self._manager._addClient(account._id, self);
+
+                    self._ignite = new ignite.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.error)
+        cb(msg.error);
+    else if (msg.ex)
+        cb(null, ex);
+    else
+        cb(null, 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/incubator-ignite/blob/3017ad52/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..842155f
--- /dev/null
+++ b/modules/control-center-web/src/main/js/agents/agent-server.js
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+/**
+ * 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()};
+
+    for (var p of cmd._params) {
+        params[p.key] = p.value;
+    }
+
+    var body = undefined;
+
+    var headers = undefined;
+
+    var method = 'GET';
+
+    if (cmd._isPost()) {
+        body = cmd.postData();
+
+        method = 'POST';
+
+        headers = {'Content-Length': body.length, '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/incubator-ignite/blob/3017ad52/modules/control-center-web/src/main/js/keys/test.crt
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/keys/test.crt b/modules/control-center-web/src/main/js/keys/test.crt
new file mode 100644
index 0000000..50c6d5c
--- /dev/null
+++ b/modules/control-center-web/src/main/js/keys/test.crt
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIIB6zCCAVQCCQDcAphbU6UcLjANBgkqhkiG9w0BAQsFADA6MRIwEAYDVQQDDAls
+b2NhbGhvc3QxJDAiBgkqhkiG9w0BCQEWFXNldmRva2ltb3ZAYXBhY2hlLm9yZzAe
+Fw0xNTA3MTQxMzAyNTNaFw0xODA2MjMxMzAyNTNaMDoxEjAQBgNVBAMMCWxvY2Fs
+aG9zdDEkMCIGCSqGSIb3DQEJARYVc2V2ZG9raW1vdkBhcGFjaGUub3JnMIGfMA0G
+CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDP/zpJrdHqCj6lPpeFF6LQtzKef6UiyBBo
+rbuOtCCgW8KMJJciluBWk2126qLt9smBN4jBpSNU3pq0r9gBMUTd/LSe7aY4D5ED
+Pjp7XsypNVKeHaHbFi7KhfHy0LYxsWiNPmmHJv4dtYOp+pGK25rkXNfyJxxjgxN6
+wo34+MnZIQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAFk9XEjcdyihws+fVmdGGUFo
+bVxI9YGH6agiNbU3WNF4B4VRzcPPW8z2mEo7eF9kgYmq/YzH4T8tgi/qkL/u8eZV
+Wmi9bg6RThLN6/hj3wVoOFKykbDQ05FFdhIJXN5UOjPmxYM97EKqg6J0W2HAb8SG
++UekPnmAo/2HTKsLykH8
+-----END CERTIFICATE-----

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3017ad52/modules/control-center-web/src/main/js/keys/test.key
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/keys/test.key b/modules/control-center-web/src/main/js/keys/test.key
new file mode 100644
index 0000000..1b395c0
--- /dev/null
+++ b/modules/control-center-web/src/main/js/keys/test.key
@@ -0,0 +1,18 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,6798185330CE2EE2
+
+sOwkmD8rvjx11l09V26dJhLhl+SyPIhyeZ3TqHXrYCATKoXlzidT+uPu1jVYtrwr
+nBLA6TrIDYRrBNlEsqGZ0cSvWTIczzVW1xZKHEJo5q2vUT/W8u/Q1QQtS3P3GeKF
+dEzx496rpZqwwVw59GNbuIwyYoVvQf3iEXzfhplGmLPELYIplDFOLgNuXQyXSGx6
+rwKsCxXMLsDyrA6DCz0Odf08p2HvWk/s5Ne3DFcQlqRNtIrBVGD2O0/Fp8ZZ2I4E
+Yn2OIIWJff3HanOjLOWKdN8YAn5UleNmlEUdIHeS5qaQ68mabOxLkSef9qglV+sd
+FHTtUq0cG6t6nhxZBziexha6v1yl/xABAHHhNPOfak+HthWxRD4N9f1yFYAeTmkn
+4kwBWoSUe12XRf2pGNqhEUKN/KhDmWk85wI55i/Cu2XmNoiBFlS9BXrRYU8uVCJw
+KlxjKTDWl1opCyvxTDxJnMkt44ZT445LRePKVueGIIKSUIXNQypOE+C1I0CL0N2W
+Ts3m9nthquvLeMx92k7b8yW69BER5uac3SIlGCOJObQXsHgyk8wYiyd/zLKfjctG
+PXieaW81UKjp+GqWpvWPz3VqnKwoyUWeVOOTviurli6kYOrHuySTMqMb6hxJctw9
+grAQTT0UPiAKWcM7InLzZnRjco+v9QLLEokjVngXPba16K/CItFY16xuGlaFLW7Y
+XTc67AkL8b76HBZelMjmCsqjvSoULhuMFwTOvUMm/mSM8rMoi9asrJRLQHRMWCST
+/6RENPLzPlOMnNLBujpBbn8V3/aYzEZsHMI+6S3d27WYlTJIqpabSA==
+-----END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3017ad52/modules/control-center-web/src/main/js/routes/agent.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/routes/agent.js b/modules/control-center-web/src/main/js/routes/agent.js
new file mode 100644
index 0000000..aad5cef
--- /dev/null
+++ b/modules/control-center-web/src/main/js/routes/agent.js
@@ -0,0 +1,37 @@
+/*
+ * 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 router = require('express').Router();
+var agentManager = require('../agents/agent-manager');
+
+/* GET summary page. */
+router.post('/topology', function(req, res) {
+    var c = agentManager.getAgentManager()._getFirstClient();
+
+    if (!c)
+        return res.status(500).send("Client not found");
+
+    var ignite = c.ignite();
+
+    ignite.cluster().then(function (cluster) {
+        res.json(cluster);
+    }, function (err) {
+        res.send(err);
+    });
+});
+
+module.exports = router;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3017ad52/modules/web-control-center/src/main/js/agents/agent-manager.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/agents/agent-manager.js b/modules/web-control-center/src/main/js/agents/agent-manager.js
deleted file mode 100644
index a35ca7a..0000000
--- a/modules/web-control-center/src/main/js/agents/agent-manager.js
+++ /dev/null
@@ -1,309 +0,0 @@
-/*
- * 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 ignite = 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) {
-        var client = 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)
-        return null;
-
-    return clientsList[0];
-};
-
-/**
- * For tests only!!!
- * @return {Client}
- */
-AgentManager.prototype.getOneClient = function() {
-    for (var userId in this._clients) {
-        if (this._clients.hasOwnProperty(userId)) {
-            var m = this._clients[userId];
-
-            if (m.length > 0)
-                return m[0];
-        }
-    }
-
-    return null;
-};
-
-
-/**
- * @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) {
-    var self = this;
-
-    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(err, ex, res) {
-        if (err)
-            cb(err);
-        else 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 {Boolean} tablesOnly
- * @param {Function} cb Callback. Take 3 arguments: {String} error, {Object} exception, {Object}
result.
- */
-Client.prototype.extractMetadata = function(jdbcDriverJarPath, jdbcDriverClass, jdbcUrl,
jdbcInfo, tablesOnly, cb) {
-    this._invokeRmtMethod('extractMetadata', 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();
-
-    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;
-
-    var account = db.Account.findByUsername(msg.login, function(err, account) {
-        if (err) {
-            self.authResult("User not found");
-        }
-        else {
-            account.authenticate(msg.password, function(err, user, res) {
-                if (!user) {
-                    self.authResult(res.message);
-                }
-                else {
-                    self.authResult(null);
-
-                    self._user = account;
-
-                    self._manager._addClient(account._id, self);
-
-                    self._ignite = new ignite.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.error)
-        cb(msg.error);
-    else if (msg.ex)
-        cb(null, ex);
-    else
-        cb(null, 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/incubator-ignite/blob/3017ad52/modules/web-control-center/src/main/js/agents/agent-server.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/agents/agent-server.js b/modules/web-control-center/src/main/js/agents/agent-server.js
deleted file mode 100644
index 842155f..0000000
--- a/modules/web-control-center/src/main/js/agents/agent-server.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.
- */
-
-/**
- * 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()};
-
-    for (var p of cmd._params) {
-        params[p.key] = p.value;
-    }
-
-    var body = undefined;
-
-    var headers = undefined;
-
-    var method = 'GET';
-
-    if (cmd._isPost()) {
-        body = cmd.postData();
-
-        method = 'POST';
-
-        headers = {'Content-Length': body.length, '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/incubator-ignite/blob/3017ad52/modules/web-control-center/src/main/js/keys/test.crt
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/keys/test.crt b/modules/web-control-center/src/main/js/keys/test.crt
deleted file mode 100644
index 50c6d5c..0000000
--- a/modules/web-control-center/src/main/js/keys/test.crt
+++ /dev/null
@@ -1,13 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIB6zCCAVQCCQDcAphbU6UcLjANBgkqhkiG9w0BAQsFADA6MRIwEAYDVQQDDAls
-b2NhbGhvc3QxJDAiBgkqhkiG9w0BCQEWFXNldmRva2ltb3ZAYXBhY2hlLm9yZzAe
-Fw0xNTA3MTQxMzAyNTNaFw0xODA2MjMxMzAyNTNaMDoxEjAQBgNVBAMMCWxvY2Fs
-aG9zdDEkMCIGCSqGSIb3DQEJARYVc2V2ZG9raW1vdkBhcGFjaGUub3JnMIGfMA0G
-CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDP/zpJrdHqCj6lPpeFF6LQtzKef6UiyBBo
-rbuOtCCgW8KMJJciluBWk2126qLt9smBN4jBpSNU3pq0r9gBMUTd/LSe7aY4D5ED
-Pjp7XsypNVKeHaHbFi7KhfHy0LYxsWiNPmmHJv4dtYOp+pGK25rkXNfyJxxjgxN6
-wo34+MnZIQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAFk9XEjcdyihws+fVmdGGUFo
-bVxI9YGH6agiNbU3WNF4B4VRzcPPW8z2mEo7eF9kgYmq/YzH4T8tgi/qkL/u8eZV
-Wmi9bg6RThLN6/hj3wVoOFKykbDQ05FFdhIJXN5UOjPmxYM97EKqg6J0W2HAb8SG
-+UekPnmAo/2HTKsLykH8
------END CERTIFICATE-----

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3017ad52/modules/web-control-center/src/main/js/keys/test.key
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/keys/test.key b/modules/web-control-center/src/main/js/keys/test.key
deleted file mode 100644
index 1b395c0..0000000
--- a/modules/web-control-center/src/main/js/keys/test.key
+++ /dev/null
@@ -1,18 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-Proc-Type: 4,ENCRYPTED
-DEK-Info: DES-EDE3-CBC,6798185330CE2EE2
-
-sOwkmD8rvjx11l09V26dJhLhl+SyPIhyeZ3TqHXrYCATKoXlzidT+uPu1jVYtrwr
-nBLA6TrIDYRrBNlEsqGZ0cSvWTIczzVW1xZKHEJo5q2vUT/W8u/Q1QQtS3P3GeKF
-dEzx496rpZqwwVw59GNbuIwyYoVvQf3iEXzfhplGmLPELYIplDFOLgNuXQyXSGx6
-rwKsCxXMLsDyrA6DCz0Odf08p2HvWk/s5Ne3DFcQlqRNtIrBVGD2O0/Fp8ZZ2I4E
-Yn2OIIWJff3HanOjLOWKdN8YAn5UleNmlEUdIHeS5qaQ68mabOxLkSef9qglV+sd
-FHTtUq0cG6t6nhxZBziexha6v1yl/xABAHHhNPOfak+HthWxRD4N9f1yFYAeTmkn
-4kwBWoSUe12XRf2pGNqhEUKN/KhDmWk85wI55i/Cu2XmNoiBFlS9BXrRYU8uVCJw
-KlxjKTDWl1opCyvxTDxJnMkt44ZT445LRePKVueGIIKSUIXNQypOE+C1I0CL0N2W
-Ts3m9nthquvLeMx92k7b8yW69BER5uac3SIlGCOJObQXsHgyk8wYiyd/zLKfjctG
-PXieaW81UKjp+GqWpvWPz3VqnKwoyUWeVOOTviurli6kYOrHuySTMqMb6hxJctw9
-grAQTT0UPiAKWcM7InLzZnRjco+v9QLLEokjVngXPba16K/CItFY16xuGlaFLW7Y
-XTc67AkL8b76HBZelMjmCsqjvSoULhuMFwTOvUMm/mSM8rMoi9asrJRLQHRMWCST
-/6RENPLzPlOMnNLBujpBbn8V3/aYzEZsHMI+6S3d27WYlTJIqpabSA==
------END RSA PRIVATE KEY-----

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3017ad52/modules/web-control-center/src/main/js/routes/agent.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/routes/agent.js b/modules/web-control-center/src/main/js/routes/agent.js
deleted file mode 100644
index 19379e2..0000000
--- a/modules/web-control-center/src/main/js/routes/agent.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 router = require('express').Router();
-var agentManager = require('../agents/agent-manager');
-
-/* GET summary page. */
-router.post('/topology', function(req, res) {
-    var c = agentManager.getAgentManager().getOneClient();
-
-    if (!c)
-        return res.status(500).send("Client not found");
-
-    var ignite = c.ignite();
-
-    ignite.cluster().then(function (cluster) {
-        res.json(cluster);
-    }, function (err) {
-        res.send(err);
-    });
-});
-
-module.exports = router;

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3017ad52/modules/web-control-center/src/main/js/views/sql.jade
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/main/js/views/sql.jade b/modules/web-control-center/src/main/js/views/sql.jade
deleted file mode 100644
index 9ba3056..0000000
--- a/modules/web-control-center/src/main/js/views/sql.jade
+++ /dev/null
@@ -1,70 +0,0 @@
-//-
-    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.
-extends ../templates/layout
-
-append scripts
-    script(src='/sql-controller.js')
-
-    script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/theme-chrome.js')
-    script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/mode-sql.js')
-    script(src='//cdnjs.cloudflare.com/ajax/libs/ace/1.2.0/ext-language_tools.js')
-
-block container
-    .row
-        .col-sm-12
-            .docs-content
-                .docs-header
-                    h1 Connect to Ignite and execute SQL queries
-                    hr
-                .docs-body(ng-controller='sqlController')
-                    .block-callout
-                        p(ng-bind-html='joinTip(screenTip)')
-                    .tabs-below(bs-tabs bs-active-pane='tabs.activeTab')
-                        div(title='Query #1' bs-pane)
-                            .row
-                                .col-sm-9
-                                    div(style='height: 200px' ui-ace='{ theme: "chrome",
mode: "sql",' +
-                                        'require: ["ace/ext/language_tools"],' +
-                                        'rendererOptions: {showPrintMargin: false, highlightGutterLine:
false, fontSize: 14},' +
-                                        'advanced: {enableSnippets: false, enableBasicAutocompletion:
true, enableLiveAutocompletion: true}}'  ng-model='query')
-                                .col-sm-3
-                                    .links(ng-hide='caches.length == 0' style='margin-top:
0.65em')
-                                        lable.labelHeader Caches:
-                                        table(st-table='caches')
-                                            tbody
-                                                tr(ng-repeat='row in caches track by row._id')
-                                                    td.col-sm-6(ng-class='{active: row._id
== selectedItem._id}')
-                                                        a(ng-click='selectItem(row)') {{$index
+ 1}}) {{row.name}}, {{row.mode | displayValue:modes:'Cache mode not set'}}
-                        div(title='Query #2' bs-pane)
-                            .row
-                    .settings-row
-                        button.btn.btn-primary(ng-click='') Explain
-                        button.btn.btn-primary(ng-click='') Execute
-                        button.btn.btn-primary(ng-click='' disabled) Scan
-                    //#resultPanel
-                    //    strong Results
-                    //
-                    //    #queryResult
-                    //        div(ng-repeat='r in results')
-                    //            .resultRow
-                    //                | {{r.id}} -> {{r.s}}
-                    //                span.props  {{r.fields}}
-
-                    div(ng-hide='results.length == 0' style='margin-top: 0.65em')
-                        lable.labelHeader Results:
-                        table.table-bordered(st-table='results')
-                            tbody
-                                tr(ng-repeat='row in results')
-                                    td
-                                        a(ng-click='selectItem(row)') {{$index + 1}}) {{row.name}},
{{row.mode | displayValue:modes:'Cache mode not set'}}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/3017ad52/modules/web-control-center/src/test/js/routes/agent.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/src/test/js/routes/agent.js b/modules/web-control-center/src/test/js/routes/agent.js
deleted file mode 100644
index 6a7fa2c..0000000
--- a/modules/web-control-center/src/test/js/routes/agent.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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 request = require('supertest'),
-    should = require('should'),
-    app = require('../../app'),
-    fs = require('fs'),
-    https = require('https'),
-    config = require('../../helpers/configuration-loader.js'),
-    agentManager = require('../../agents/agent-manager');
-
-/**
- * Create HTTP server.
- */
-/**
- * Start agent server.
- */
-var agentServer = https.createServer({
-    key: fs.readFileSync(config.get('monitor:server:key')),
-    cert: fs.readFileSync(config.get('monitor:server:cert')),
-    passphrase: config.get('monitor:server:keyPassphrase')
-});
-
-agentServer.listen(config.get('monitor:server:port'));
-
-agentManager.createManager(agentServer);
-
-describe('request from agent', function() {
-    var agent = request.agent(app);
-
-    before(function (done) {
-        this.timeout(10000);
-
-        agent
-            .post('/login')
-            .send({email: 'anovikov@gridgain.com', password: 'extHB2aXgb'})
-            .expect(302)
-            .end(function (err) {
-                if (err)
-                    throw err;
-
-                setTimeout(done, 5000);
-            });
-    });
-
-    it('should return topology snapshot', function(done){
-        agent
-            .post('/agent/topology')
-            .send({})
-            .end(function(err, nodes) {
-                if (err) {
-                    console.log(err.response.text);
-
-                    throw err;
-                }
-
-                console.log(nodes);
-
-                done();
-            });
-    });
-
-    //it('should query result', function(done){
-    //    agent
-    //        .post('/agent/query')
-    //        .send({
-    //            username: 'nva',
-    //            password: 'nva.141',
-    //            host: 'localhost',
-    //            port: '5432',
-    //            dbName: 'ggmonitor'
-    //        })
-    //        .end(function(err, res) {
-    //            if (err)
-    //                throw err;
-    //
-    //            done();
-    //        });
-    //});
-});
\ No newline at end of file



Mime
View raw message