You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2016/09/13 09:52:54 UTC

[02/69] [abbrv] ignite git commit: Web Console beta-3.

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/src/main/js/views/templates/getting-started.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/templates/getting-started.jade b/modules/web-console/src/main/js/views/templates/getting-started.jade
deleted file mode 100644
index 98bc265..0000000
--- a/modules/web-console/src/main/js/views/templates/getting-started.jade
+++ /dev/null
@@ -1,32 +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.
-
-.modal.center(role='dialog')
-    .modal-dialog
-        .modal-content
-            #errors-container.modal-header.header
-                button.close(ng-click='close()' aria-hidden='true') ×
-                h4.modal-title {{title}}
-            .getting-started
-                .col-xs-12(ng-bind-html='message')
-            .modal-footer
-                .checkbox
-                    label
-                        input(type='checkbox' ng-model='ui.showGettingStarted')
-                        | Show getting started on next login
-                a.btn.btn-primary(ng-disabled='isFirst()' ng-click='!isFirst() && prev()') Prev
-                a.btn.btn-primary(ng-disabled='isLast()' ng-click='!isLast() && next()') Next
-                a.btn.btn-primary(ng-click='close()') Close

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/src/main/js/views/templates/message.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/templates/message.jade b/modules/web-console/src/main/js/views/templates/message.jade
deleted file mode 100644
index 6dcf445..0000000
--- a/modules/web-console/src/main/js/views/templates/message.jade
+++ /dev/null
@@ -1,26 +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.
-
-.modal(tabindex='-1' role='dialog')
-    .modal-dialog
-        .modal-content
-            .modal-header
-                button.close(ng-click='$hide()' aria-hidden='true') ×
-                h4.modal-title {{title}}
-            .modal-body(ng-show='content')
-                p(ng-bind-html='content.join("<br/>")' style='text-align: left;')
-            .modal-footer
-                button.btn.btn-primary(id='confirm-btn-confirm' ng-click='$hide()') Ok

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/src/main/js/views/templates/pagination.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/templates/pagination.jade b/modules/web-console/src/main/js/views/templates/pagination.jade
deleted file mode 100644
index 08ced60..0000000
--- a/modules/web-console/src/main/js/views/templates/pagination.jade
+++ /dev/null
@@ -1,32 +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.
-
-nav(ng-if='numPages && pages.length >= 2')
-    ul.pagination
-        li(ng-if='currentPage > 1')
-            a(href='javascript:void(0);' ng-click='selectPage(1)' bs-tooltip='' data-title='First page' data-placement='bottom')
-                i.fa.fa-angle-double-left
-        li(ng-if='currentPage > 1')
-            a(href='javascript:void(0);' ng-click='selectPage(currentPage-1)' bs-tooltip='' data-title='Previous page' data-placement='bottom')
-                i.fa.fa-angle-left
-        li(ng-repeat='page in pages' ng-class='{active: page==currentPage}')
-            a(href='javascript:void(0);' ng-click='selectPage(page)') {{page}}
-        li(ng-if='currentPage < numPages')
-            a(href='javascript:void(0);' ng-click='selectPage(currentPage+1)' bs-tooltip='' data-title='Next page' data-placement='bottom')
-                i.fa.fa-angle-right
-        li(ng-if='currentPage < numPages')
-            a(href='javascript:void(0);' ng-click='selectPage(numPages)' bs-tooltip='' data-title='Last page' data-placement='bottom')
-                i.fa.fa-angle-double-right
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/src/main/js/views/templates/select.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/templates/select.jade b/modules/web-console/src/main/js/views/templates/select.jade
deleted file mode 100644
index 3feee61..0000000
--- a/modules/web-console/src/main/js/views/templates/select.jade
+++ /dev/null
@@ -1,26 +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.
-
-ul.select.dropdown-menu(tabindex='-1' ng-show='$isVisible()' role='select')
-    li(ng-if='$showAllNoneButtons || ($isMultiple && $matches.length > 2)')
-        a(id='li-dropdown-select-all' ng-click='$selectAll()') {{$allText}} ({{$matches.length}})
-        a(id='li-dropdown-select-none' ng-click='$selectNone()') {{$noneText}}
-        hr(style='margin: 5px 0')
-    li(role='presentation' ng-repeat='match in $matches')
-        hr(ng-if='match.value == undefined' style='margin: 5px 0')
-        a(id='li-dropdown-item-{{$index}}'  role='menuitem' tabindex='-1' ng-class='{active: $isActive($index)}' ng-click='$select($index, $event)' bs-tooltip='widthIsSufficient && !widthIsSufficient("li-dropdown-item-{{$index}}", $index, match.label) ? match.label : ""' data-placement='bottom')
-            i(class='{{$iconCheckmark}}' ng-if='$isActive($index)' ng-class='{active: $isActive($index)}')
-            span(ng-bind='match.label')

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/src/main/js/views/templates/validation-error.jade
----------------------------------------------------------------------
diff --git a/modules/web-console/src/main/js/views/templates/validation-error.jade b/modules/web-console/src/main/js/views/templates/validation-error.jade
deleted file mode 100644
index 13deb9b..0000000
--- a/modules/web-console/src/main/js/views/templates/validation-error.jade
+++ /dev/null
@@ -1,25 +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.
-
-.popover.validation-error
-    .arrow
-    .popover-content
-        table
-            tr
-                td
-                    label {{content}}&nbsp&nbsp
-                td
-                    button.close(id='popover-btn-close' ng-click='$hide()') &times;

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/src/test/js/routes/agent.js
----------------------------------------------------------------------
diff --git a/modules/web-console/src/test/js/routes/agent.js b/modules/web-console/src/test/js/routes/agent.js
deleted file mode 100644
index 1c4aff5..0000000
--- a/modules/web-console/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: 'test@test.com', password: 'test'})
-            .expect(302)
-            .end(function (err) {
-                if (error)
-                    throw error;
-
-                setTimeout(done, 5000);
-            });
-    });
-
-    it('should return topology snapshot', function(done){
-        agent
-            .post('/agent/topology')
-            .send({})
-            .end(function(err, nodes) {
-                if (error) {
-                    console.log(error.response.text);
-
-                    throw error;
-                }
-
-                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();
-    //        });
-    //});
-});

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/.gitignore
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/.gitignore b/modules/web-console/web-agent/.gitignore
new file mode 100644
index 0000000..57dd45e
--- /dev/null
+++ b/modules/web-console/web-agent/.gitignore
@@ -0,0 +1,2 @@
+logs/*.log.*
+jdbc-drivers/*.jar

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/README.txt
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/README.txt b/modules/web-console/web-agent/README.txt
new file mode 100644
index 0000000..c6e625b
--- /dev/null
+++ b/modules/web-console/web-agent/README.txt
@@ -0,0 +1,88 @@
+Ignite Web Agent
+======================================
+Ignite Web Agent is a java standalone application that allow to connect Ignite Grid to Ignite Web Console.
+Ignite Web Agent communicates with grid nodes via REST interface and connects to Ignite Web Console via web-socket.
+
+Two main functions of Ignite Web Agent:
+ 1. Proxy between Ignite Web Console and Ignite Grid to execute SQL statements and collect metrics for monitoring.
+   You may need to specify URI for connect to Ignite REST server via "-n" option.
+
+ 2. Proxy between Ignite Web Console and user RDBMS to collect database metadata for later CacheTypeMetadata configuration.
+   You may need to copy JDBC driver into "./jdbc-drivers" subfolder or specify path via "-d" option.
+
+Usage example:
+  ignite-web-agent.sh
+
+Configuration file:
+  Should be a file with simple line-oriented format as described here: http://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#load(java.io.Reader)
+
+  Available entries names:
+    tokens
+    server-uri
+    node-uri
+    driver-folder
+
+  Example configuration file:
+    tokens=1a2b3c4d5f,2j1s134d12
+    serverURI=https://console.example.com:3001
+
+Security tokens:
+  1) By default security token of current user will be included into "default.properties" inside downloaded "ignite-web-agent-x.x.x.zip".
+  2) One can get/reset token in Web Console profile (https://<your_console_address>/settings/profile).
+  3) One may specify several comma separated tokens using configuration file or command line arguments of web agent.
+
+Ignite Web agent requirements:
+  1) In order to communicate with web agent Ignite node should be started with REST server (move ignite-rest-http folder from lib/optional/ to lib/).
+  2) Configure web agent serverURI property by Ignite node REST server URI.
+
+Options:
+  -h, --help
+     Print this help message
+  -c, --config
+     Path to configuration file
+  -d, --driver-folder
+     Path to folder with JDBC drivers, default value: ./jdbc-drivers
+  -n, --node-uri
+     URI for connect to Ignite REST server, default value:
+     http://localhost:8080
+  -s, --server-uri
+     URI for connect to Ignite Web Console via web-socket protocol, default
+     value: http://localhost:3001
+  -t, --tokens
+     User's security tokens
+
+How to build:
+  To build from sources run following command in Ignite project root folder:
+  mvn clean package -pl :ignite-web-agent -am -P web-console -DskipTests=true
+
+Demo of Ignite Web Agent:
+ In order to simplify evaluation demo mode was implemented. To start demo, you need to click button "Start demo".
+ New tab will be open with prepared demo data.
+
+ 1) Demo for import domain model from database.
+   In this mode an in-memory H2 database will be started.
+   How to evaluate:
+     1.1) Go to Ignite Web Console "Domain model" screen.
+     1.2) Click "Import from database". You should see modal with demo description.
+     1.3) Click "Next" button. You should see list of available schemas.
+     1.4) Click "Next" button. You should see list of available tables.
+     1.5) Click "Next" button. You should see import options.
+     1.6) Select some of them and click "Save".
+
+   2) Demo for SQL.
+     How to evaluate:
+     In this mode internal Ignite node will be started. Cache created and populated with data.
+       2.1) Click "SQL" in Ignite Web Console top menu.
+       2.2) "Demo" notebook with preconfigured queries will be opened.
+       2.3) You can also execute any SQL queries for tables: "Country, Department, Employee, Parking, Car".
+
+ For example:
+   2.4) Enter SQL statement:
+           SELECT p.name, count(*) AS cnt
+           FROM "ParkingCache".Parking p
+           INNER JOIN "CarCache".Car c
+             ON (p.id) = (c.parkingId)
+           GROUP BY P.NAME
+   2.5) Click "Execute" button. You should get some data in table.
+   2.6) Click charts buttons to see auto generated charts.
+

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/assembly/release-web-agent.xml
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/assembly/release-web-agent.xml b/modules/web-console/web-agent/assembly/release-web-agent.xml
new file mode 100644
index 0000000..bb994c0
--- /dev/null
+++ b/modules/web-console/web-agent/assembly/release-web-agent.xml
@@ -0,0 +1,66 @@
+<?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.
+-->
+
+<assembly xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+          xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
+          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
+    <id>release-ignite-web-agent</id>
+
+    <formats>
+        <format>zip</format>
+    </formats>
+
+    <fileSets>
+        <fileSet>
+            <directory>${basedir}</directory>
+            <outputDirectory>/</outputDirectory>
+            <includes>
+                <include>jdbc-drivers/README*</include>
+                <include>demo/README*</include>
+                <include>demo/*.sql</include>
+                <include>logs/README*</include>
+                <include>README*</include>
+                <include>LICENSE*</include>
+                <include>NOTICE*</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>${basedir}/bin</directory>
+            <outputDirectory>/</outputDirectory>
+            <includes>
+                <include>**/*.bat</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>${basedir}/bin</directory>
+            <outputDirectory>/</outputDirectory>
+            <fileMode>0755</fileMode>
+            <includes>
+                <include>**/*.sh</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>${project.build.directory}</directory>
+            <outputDirectory>/</outputDirectory>
+            <includes>
+                <include>ignite-web-agent-${project.version}.jar</include>
+            </includes>
+        </fileSet>
+    </fileSets>
+</assembly>

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/bin/ignite-web-agent.bat
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/bin/ignite-web-agent.bat b/modules/web-console/web-agent/bin/ignite-web-agent.bat
new file mode 100644
index 0000000..f16eb35
--- /dev/null
+++ b/modules/web-console/web-agent/bin/ignite-web-agent.bat
@@ -0,0 +1,70 @@
+::
+:: 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.
+::
+
+@echo off
+Setlocal EnableDelayedExpansion
+
+if "%OS%" == "Windows_NT"  setlocal
+
+:: Check JAVA_HOME.
+if defined JAVA_HOME  goto checkJdk
+    echo %0, ERROR:
+    echo JAVA_HOME environment variable is not found.
+    echo Please point JAVA_HOME variable to location of JDK 1.7 or JDK 1.8.
+    echo You can also download latest JDK at http://java.com/download.
+goto error_finish
+
+:checkJdk
+:: Check that JDK is where it should be.
+if exist "%JAVA_HOME%\bin\java.exe" goto checkJdkVersion
+    echo %0, ERROR:
+    echo JAVA is not found in JAVA_HOME=%JAVA_HOME%.
+    echo Please point JAVA_HOME variable to installation of JDK 1.7 or JDK 1.8.
+    echo You can also download latest JDK at http://java.com/download.
+goto error_finish
+
+:checkJdkVersion
+"%JAVA_HOME%\bin\java.exe" -version 2>&1 | findstr "1\.[78]\." > nul
+if %ERRORLEVEL% equ 0 goto run_java
+    echo %0, ERROR:
+    echo The version of JAVA installed in %JAVA_HOME% is incorrect.
+    echo Please point JAVA_HOME variable to installation of JDK 1.7 or JDK 1.8.
+    echo You can also download latest JDK at http://java.com/download.
+goto error_finish
+
+:run_java
+
+::
+:: JVM options. See http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp for more details.
+::
+:: ADD YOUR/CHANGE ADDITIONAL OPTIONS HERE
+::
+if "%JVM_OPTS%" == "" set JVM_OPTS=-Xms1g -Xmx1g -server -XX:+AggressiveOpts -XX:MaxPermSize=256m
+
+"%JAVA_HOME%\bin\java.exe" %JVM_OPTS% -cp "*" org.apache.ignite.console.agent.AgentLauncher  %*
+
+set JAVA_ERRORLEVEL=%ERRORLEVEL%
+
+:: errorlevel 130 if aborted with Ctrl+c
+if %JAVA_ERRORLEVEL%==130 goto eof
+
+:error_finish
+
+if not "%NO_PAUSE%" == "1" pause
+
+goto :eof
+

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/bin/ignite-web-agent.sh
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/bin/ignite-web-agent.sh b/modules/web-console/web-agent/bin/ignite-web-agent.sh
new file mode 100644
index 0000000..3f2c2bc
--- /dev/null
+++ b/modules/web-console/web-agent/bin/ignite-web-agent.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+#
+# 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.
+#
+
+# Check JAVA_HOME.
+if [ "$JAVA_HOME" = "" ]; then
+    JAVA=`type -p java`
+    RETCODE=$?
+
+    if [ $RETCODE -ne 0 ]; then
+        echo $0", ERROR:"
+        echo "JAVA_HOME environment variable is not found."
+        echo "Please point JAVA_HOME variable to location of JDK 1.7 or JDK 1.8."
+        echo "You can also download latest JDK at http://java.com/download"
+
+        exit 1
+    fi
+
+    JAVA_HOME=
+else
+    JAVA=${JAVA_HOME}/bin/java
+fi
+
+#
+# Check JDK.
+#
+if [ ! -e "$JAVA" ]; then
+    echo $0", ERROR:"
+    echo "JAVA is not found in JAVA_HOME=$JAVA_HOME."
+    echo "Please point JAVA_HOME variable to installation of JDK 1.7 or JDK 1.8."
+    echo "You can also download latest JDK at http://java.com/download"
+
+    exit 1
+fi
+
+JAVA_VER=`"$JAVA" -version 2>&1 | egrep "1\.[78]\."`
+
+if [ "$JAVA_VER" == "" ]; then
+    echo $0", ERROR:"
+    echo "The version of JAVA installed in JAVA_HOME=$JAVA_HOME is incorrect."
+    echo "Please point JAVA_HOME variable to installation of JDK 1.7 or JDK 1.8."
+    echo "You can also download latest JDK at http://java.com/download"
+
+    exit 1
+fi
+
+SOURCE="${BASH_SOURCE[0]}"
+
+DIR="$( dirname "$SOURCE" )"
+
+while [ -h "$SOURCE" ]
+    do
+        SOURCE="$(readlink "$SOURCE")"
+
+        [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
+
+        DIR="$( cd -P "$( dirname "$SOURCE"  )" && pwd )"
+    done
+
+DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
+
+cd $DIR
+
+#
+# JVM options. See http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp for more details.
+#
+# ADD YOUR/CHANGE ADDITIONAL OPTIONS HERE
+#
+if [ -z "$JVM_OPTS" ] ; then
+    JVM_OPTS="-Xms1g -Xmx1g -server -XX:+AggressiveOpts -XX:MaxPermSize=256m"
+fi
+
+"$JAVA" ${JVM_OPTS} -cp "*" org.apache.ignite.console.agent.AgentLauncher "$@"

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/demo/README.txt
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/demo/README.txt b/modules/web-console/web-agent/demo/README.txt
new file mode 100644
index 0000000..17e5074
--- /dev/null
+++ b/modules/web-console/web-agent/demo/README.txt
@@ -0,0 +1,4 @@
+Ignite Web Agent
+======================================
+
+This is folder for demo files.

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/demo/db-init.sql
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/demo/db-init.sql b/modules/web-console/web-agent/demo/db-init.sql
new file mode 100644
index 0000000..0688ea0
--- /dev/null
+++ b/modules/web-console/web-agent/demo/db-init.sql
@@ -0,0 +1,102 @@
+--
+--  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.
+--
+
+CREATE TABLE COUNTRY (
+    ID         INTEGER NOT NULL PRIMARY KEY,
+    NAME       VARCHAR(50),
+    POPULATION INTEGER NOT NULL
+);
+
+CREATE TABLE DEPARTMENT (
+    ID         INTEGER NOT NULL PRIMARY KEY,
+    COUNTRY_ID INTEGER NOT NULL,
+    NAME       VARCHAR(50) NOT NULL
+);
+
+CREATE TABLE EMPLOYEE (
+    ID            INTEGER NOT NULL PRIMARY KEY,
+    DEPARTMENT_ID INTEGER NOT NULL,
+    MANAGER_ID    INTEGER,
+    FIRST_NAME    VARCHAR(50) NOT NULL,
+    LAST_NAME     VARCHAR(50) NOT NULL,
+    EMAIL         VARCHAR(50) NOT NULL,
+    PHONE_NUMBER  VARCHAR(50),
+    HIRE_DATE     DATE        NOT NULL,
+    JOB           VARCHAR(50) NOT NULL,
+    SALARY        DOUBLE
+);
+
+CREATE INDEX EMP_SALARY ON EMPLOYEE (SALARY ASC);
+CREATE INDEX EMP_NAMES ON EMPLOYEE (FIRST_NAME ASC, LAST_NAME ASC);
+
+CREATE SCHEMA CARS;
+
+CREATE TABLE CARS.PARKING (
+    ID       INTEGER     NOT NULL PRIMARY KEY,
+    NAME     VARCHAR(50) NOT NULL,
+    CAPACITY INTEGER NOT NULL
+);
+
+CREATE TABLE CARS.CAR (
+    ID         INTEGER NOT NULL PRIMARY KEY,
+    PARKING_ID INTEGER NOT NULL,
+    NAME       VARCHAR(50) NOT NULL
+);
+
+INSERT INTO COUNTRY(ID, NAME, POPULATION) VALUES(0, 'Country #1', 10000000);
+INSERT INTO COUNTRY(ID, NAME, POPULATION) VALUES(1, 'Country #2', 20000000);
+INSERT INTO COUNTRY(ID, NAME, POPULATION) VALUES(2, 'Country #3', 30000000);
+
+INSERT INTO DEPARTMENT(ID, COUNTRY_ID, NAME) VALUES(0, 0, 'Department #1');
+INSERT INTO DEPARTMENT(ID, COUNTRY_ID, NAME) VALUES(1, 0, 'Department #2');
+INSERT INTO DEPARTMENT(ID, COUNTRY_ID, NAME) VALUES(2, 2, 'Department #3');
+INSERT INTO DEPARTMENT(ID, COUNTRY_ID, NAME) VALUES(3, 1, 'Department #4');
+INSERT INTO DEPARTMENT(ID, COUNTRY_ID, NAME) VALUES(4, 1, 'Department #5');
+INSERT INTO DEPARTMENT(ID, COUNTRY_ID, NAME) VALUES(5, 1, 'Department #6');
+
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(0, 0, 'First name manager #1', 'Last name manager #1', 'Email manager #1', 'Phone number manager #1', '2014-01-01', 'Job manager #1', 1100.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(1, 1, 'First name manager #2', 'Last name manager #2', 'Email manager #2', 'Phone number manager #2', '2014-01-01', 'Job manager #2', 2100.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(2, 2, 'First name manager #3', 'Last name manager #3', 'Email manager #3', 'Phone number manager #3', '2014-01-01', 'Job manager #3', 3100.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(3, 3, 'First name manager #4', 'Last name manager #4', 'Email manager #4', 'Phone number manager #4', '2014-01-01', 'Job manager #4', 1500.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(4, 4, 'First name manager #5', 'Last name manager #5', 'Email manager #5', 'Phone number manager #5', '2014-01-01', 'Job manager #5', 1700.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(5, 5, 'First name manager #6', 'Last name manager #6', 'Email manager #6', 'Phone number manager #6', '2014-01-01', 'Job manager #6', 1300.00);
+
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(101, 0, 0, 'First name employee #1', 'Last name employee #1', 'Email employee #1', 'Phone number employee #1', '2014-01-01', 'Job employee #1', 600.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(102, 0, 0, 'First name employee #2', 'Last name employee #2', 'Email employee #2', 'Phone number employee #2', '2014-01-01', 'Job employee #2', 1600.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(103, 1, 1, 'First name employee #3', 'Last name employee #3', 'Email employee #3', 'Phone number employee #3', '2014-01-01', 'Job employee #3', 2600.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(104, 2, 2, 'First name employee #4', 'Last name employee #4', 'Email employee #4', 'Phone number employee #4', '2014-01-01', 'Job employee #4', 1000.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(105, 2, 2, 'First name employee #5', 'Last name employee #5', 'Email employee #5', 'Phone number employee #5', '2014-01-01', 'Job employee #5', 1200.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(106, 2, 2, 'First name employee #6', 'Last name employee #6', 'Email employee #6', 'Phone number employee #6', '2014-01-01', 'Job employee #6', 800.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(107, 3, 3, 'First name employee #7', 'Last name employee #7', 'Email employee #7', 'Phone number employee #7', '2014-01-01', 'Job employee #7', 1400.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(108, 4, 4, 'First name employee #8', 'Last name employee #8', 'Email employee #8', 'Phone number employee #8', '2014-01-01', 'Job employee #8', 800.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(109, 4, 4, 'First name employee #9', 'Last name employee #9', 'Email employee #9', 'Phone number employee #9', '2014-01-01', 'Job employee #9', 1490.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(110, 4, 4, 'First name employee #10', 'Last name employee #12', 'Email employee #10', 'Phone number employee #10', '2014-01-01', 'Job employee #10', 1600.00);
+INSERT INTO EMPLOYEE(ID, DEPARTMENT_ID, MANAGER_ID, FIRST_NAME, LAST_NAME, EMAIL, PHONE_NUMBER, HIRE_DATE, JOB, SALARY) VALUES(111, 5, 5, 'First name employee #11', 'Last name employee #11', 'Email employee #11', 'Phone number employee #11', '2014-01-01', 'Job employee #11', 400.00);
+
+INSERT INTO CARS.PARKING(ID, NAME, CAPACITY) VALUES(0, 'Parking #1', 10);
+INSERT INTO CARS.PARKING(ID, NAME, CAPACITY) VALUES(1, 'Parking #2', 20);
+INSERT INTO CARS.PARKING(ID, NAME, CAPACITY) VALUES(2, 'Parking #3', 30);
+
+INSERT INTO CARS.CAR(ID, PARKING_ID, NAME) VALUES(0, 0, 'Car #1');
+INSERT INTO CARS.CAR(ID, PARKING_ID, NAME) VALUES(1, 0, 'Car #2');
+INSERT INTO CARS.CAR(ID, PARKING_ID, NAME) VALUES(2, 0, 'Car #3');
+INSERT INTO CARS.CAR(ID, PARKING_ID, NAME) VALUES(3, 1, 'Car #4');
+INSERT INTO CARS.CAR(ID, PARKING_ID, NAME) VALUES(4, 1, 'Car #5');
+INSERT INTO CARS.CAR(ID, PARKING_ID, NAME) VALUES(5, 2, 'Car #6');
+INSERT INTO CARS.CAR(ID, PARKING_ID, NAME) VALUES(6, 2, 'Car #7');
+INSERT INTO CARS.CAR(ID, PARKING_ID, NAME) VALUES(7, 2, 'Car #8');
+INSERT INTO CARS.CAR(ID, PARKING_ID, NAME) VALUES(8, 2, 'Car #9');

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/jdbc-drivers/README.txt
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/jdbc-drivers/README.txt b/modules/web-console/web-agent/jdbc-drivers/README.txt
new file mode 100644
index 0000000..cad43b7
--- /dev/null
+++ b/modules/web-console/web-agent/jdbc-drivers/README.txt
@@ -0,0 +1,10 @@
+Ignite Web Agent
+======================================
+
+If you are are planning to load cache type metadata from your existing databases
+you need to copy JDBC drivers in this folder.
+
+This is default folder for JDBC drivers.
+
+Also, you could specify custom folder using option: "-d CUSTOM_PATH_TO_FOLDER_WITH_JDBC_DRIVERS".
+

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/logs/README.txt
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/logs/README.txt b/modules/web-console/web-agent/logs/README.txt
new file mode 100644
index 0000000..3a220eb
--- /dev/null
+++ b/modules/web-console/web-agent/logs/README.txt
@@ -0,0 +1,5 @@
+Ignite Web Agent
+======================================
+
+This is folder for agent logs.
+

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/pom.xml
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/pom.xml b/modules/web-console/web-agent/pom.xml
new file mode 100644
index 0000000..530a272
--- /dev/null
+++ b/modules/web-console/web-agent/pom.xml
@@ -0,0 +1,199 @@
+<?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="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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-web-agent</artifactId>
+    <packaging>jar</packaging>
+    <version>1.7.0-SNAPSHOT</version>
+    <url>http://ignite.apache.org</url>
+
+    <properties>
+        <maven.build.timestamp.format>yyMMddHHmmss</maven.build.timestamp.format>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.socket</groupId>
+            <artifactId>socket.io-client</artifactId>
+            <version>0.7.0</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.fasterxml.jackson.datatype</groupId>
+            <artifactId>jackson-datatype-json-org</artifactId>
+            <version>${jackson2.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.beust</groupId>
+            <artifactId>jcommander</artifactId>
+            <version>1.48</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+            <version>${httpclient.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-schema-import-db</artifactId>
+            <version>${project.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.gridgain</groupId>
+                    <artifactId>ignite-shmem</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-indexing</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-rest-http</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-spring</artifactId>
+            <version>${project.version}</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-aop</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-tx</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework</groupId>
+                    <artifactId>spring-jdbc</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-log4j</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <finalName>ignite-web-agent-${project.version}</finalName>
+
+        <plugins>
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.5</version>
+
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <mainClass>org.apache.ignite.console.agent.AgentLauncher</mainClass>
+                        </manifest>
+                        <manifestEntries>
+                            <Build-Time>${maven.build.timestamp}</Build-Time>
+                        </manifestEntries>
+                    </archive>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>2.4</version>
+
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+
+                        <configuration>
+                            <createDependencyReducedPom>false</createDependencyReducedPom>
+                            <filters>
+                                <filter>
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>META-INF/maven/**</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <version>2.4</version>
+                <inherited>false</inherited>
+
+                <executions>
+                    <execution>
+                        <id>release-web-agent</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <descriptors>
+                                <descriptor>assembly/release-web-agent.xml</descriptor>
+                            </descriptors>
+                            <finalName>ignite-web-agent-${project.version}</finalName>
+                            <outputDirectory>target</outputDirectory>
+                            <appendAssemblyId>false</appendAssemblyId>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java
new file mode 100644
index 0000000..d4787cc
--- /dev/null
+++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentConfiguration.java
@@ -0,0 +1,268 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.console.agent;
+
+import com.beust.jcommander.Parameter;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Agent configuration.
+ */
+public class AgentConfiguration {
+    /** Default server port. */
+    public static final int DFLT_SERVER_PORT = 3001;
+
+    /** Default Ignite node HTTP port. */
+    public static final int DFLT_NODE_PORT = 8080;
+
+    /** Default path to agent property file. */
+    public static final String DFLT_CFG_PATH = "default.properties";
+
+    /** Default server URI. */
+    private static final String DFLT_SERVER_URI = "http://localhost:3001";
+
+    /** Default Ignite node HTTP URI. */
+    private static final String DFLT_NODE_URI = "http://localhost:8080";
+
+    /** */
+    @Parameter(names = {"-t", "--tokens"},
+        description = "User's tokens separated by comma used to connect to Ignite Console.")
+    private List<String> tokens;
+
+    /** */
+    @Parameter(names = {"-s", "--server-uri"},
+        description = "URI for connect to Ignite Console via web-socket protocol" +
+        "           " +
+        "      Default value: " + DFLT_SERVER_URI)
+    private String srvUri;
+
+    /** */
+    @Parameter(names = {"-n", "--node-uri"}, description = "URI for connect to Ignite node REST server" +
+        "                        " +
+        "      Default value: " + DFLT_NODE_URI)
+    private String nodeUri;
+
+    /** URI for connect to Ignite demo node REST server */
+    private String demoNodeUri;
+
+    /** */
+    @Parameter(names = {"-c", "--config"}, description = "Path to agent property file" +
+        "                                  " +
+        "      Default value: " + DFLT_CFG_PATH)
+    private String cfgPath;
+
+    /** */
+    @Parameter(names = {"-d", "--driver-folder"}, description = "Path to folder with JDBC drivers" +
+        "                             " +
+        "      Default value: ./jdbc-drivers")
+    private String driversFolder;
+
+    /** */
+    @Parameter(names = { "-h", "--help" }, help = true, description = "Print this help message")
+    private Boolean help;
+
+    /**
+     * @return Tokens.
+     */
+    public List<String> tokens() {
+        return tokens;
+    }
+
+    /**
+     * @param tokens Tokens.
+     */
+    public void tokens(List<String> tokens) {
+        this.tokens = tokens;
+    }
+
+    /**
+     * @return Server URI.
+     */
+    public String serverUri() {
+        return srvUri;
+    }
+
+    /**
+     * @param srvUri URI.
+     */
+    public void serverUri(String srvUri) {
+        this.srvUri = srvUri;
+    }
+
+    /**
+     * @return Node URI.
+     */
+    public String nodeUri() {
+        return nodeUri;
+    }
+
+    /**
+     * @param nodeUri Node URI.
+     */
+    public void nodeUri(String nodeUri) {
+        this.nodeUri = nodeUri;
+    }
+
+    /**
+     * @return Demo node URI.
+     */
+    public String demoNodeUri() {
+        return demoNodeUri;
+    }
+
+    /**
+     * @param demoNodeUri Demo node URI.
+     */
+    public void demoNodeUri(String demoNodeUri) {
+        this.demoNodeUri = demoNodeUri;
+    }
+
+    /**
+     * @return Configuration path.
+     */
+    public String configPath() {
+        return cfgPath == null ? DFLT_CFG_PATH : cfgPath;
+    }
+
+    /**
+     * @return Configured drivers folder.
+     */
+    public String driversFolder() {
+        return driversFolder;
+    }
+
+    /**
+     * @param driversFolder Driver folder.
+     */
+    public void driversFolder(String driversFolder) {
+        this.driversFolder = driversFolder;
+    }
+
+    /**
+     * @return {@code true} If agent options usage should be printed.
+     */
+    public Boolean help() {
+        return help != null ? help : Boolean.FALSE;
+    }
+
+    /**
+     * @param cfgUrl URL.
+     */
+    public void load(URL cfgUrl) throws IOException {
+        Properties props = new Properties();
+
+        try (Reader reader = new InputStreamReader(cfgUrl.openStream())) {
+            props.load(reader);
+        }
+
+        String val = (String)props.remove("tokens");
+
+        if (val != null)
+            tokens(Arrays.asList(val.split(",")));
+
+        val = (String)props.remove("server-uri");
+
+        if (val != null)
+            serverUri(val);
+
+        val = (String)props.remove("node-uri");
+
+        if (val != null)
+            nodeUri(val);
+
+        val = (String)props.remove("driver-folder");
+
+        if (val != null)
+            driversFolder(val);
+    }
+
+    /**
+     * @param cmd Command.
+     */
+    public void merge(AgentConfiguration cmd) {
+        if (tokens == null)
+            tokens(cmd.tokens());
+
+        if (srvUri == null)
+            serverUri(cmd.serverUri());
+
+        if (srvUri == null)
+            serverUri(DFLT_SERVER_URI);
+
+        if (nodeUri == null)
+            nodeUri(cmd.nodeUri());
+
+        if (nodeUri == null)
+            nodeUri(DFLT_NODE_URI);
+
+        if (driversFolder == null)
+            driversFolder(cmd.driversFolder());
+    }
+
+    /** {@inheritDoc} */
+    @Override public String toString() {
+        StringBuilder sb = new StringBuilder();
+
+        if (tokens != null && tokens.size() > 0) {
+            sb.append("User's security tokens        : ");
+
+            boolean first = true;
+
+            for (String tok : tokens) {
+                if (first)
+                    first = false;
+                else
+                    sb.append(",");
+
+                if (tok.length() > 4) {
+                    sb.append(new String(new char[tok.length() - 4]).replace('\0', '*'));
+
+                    sb.append(tok.substring(tok.length() - 4));
+                }
+                else
+                    sb.append(new String(new char[tok.length()]).replace('\0', '*'));
+            }
+
+            sb.append('\n');
+        }
+
+        sb.append("URI to Ignite node REST server: ").append(nodeUri == null ? DFLT_NODE_URI : nodeUri).append('\n');
+        sb.append("URI to Ignite Console server  : ").append(srvUri == null ? DFLT_SERVER_URI : srvUri).append('\n');
+        sb.append("Path to agent property file   : ").append(configPath()).append('\n');
+
+        String drvFld = driversFolder();
+
+        if (drvFld == null) {
+            File agentHome = AgentUtils.getAgentHome();
+
+            if (agentHome != null)
+                drvFld = new File(agentHome, "jdbc-drivers").getPath();
+        }
+
+        sb.append("Path to JDBC drivers folder   : ").append(drvFld);
+
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java
new file mode 100644
index 0000000..810fad4
--- /dev/null
+++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentLauncher.java
@@ -0,0 +1,344 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.console.agent;
+
+import com.beust.jcommander.JCommander;
+import com.beust.jcommander.ParameterException;
+import io.socket.client.Ack;
+import io.socket.client.IO;
+import io.socket.client.Socket;
+import io.socket.emitter.Emitter;
+import java.io.File;
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import org.apache.ignite.console.agent.handlers.DatabaseHandler;
+import org.apache.ignite.console.agent.handlers.RestHandler;
+import org.apache.ignite.internal.util.typedef.X;
+import org.apache.log4j.Logger;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import static io.socket.client.Socket.EVENT_CONNECT;
+import static io.socket.client.Socket.EVENT_CONNECTING;
+import static io.socket.client.Socket.EVENT_CONNECT_ERROR;
+import static io.socket.client.Socket.EVENT_DISCONNECT;
+import static io.socket.client.Socket.EVENT_ERROR;
+import static io.socket.client.Socket.EVENT_RECONNECTING;
+import static org.apache.ignite.console.agent.AgentConfiguration.DFLT_SERVER_PORT;
+
+/**
+ * Control Center Agent launcher.
+ */
+public class AgentLauncher {
+    /** */
+    private static final Logger log = Logger.getLogger(AgentLauncher.class.getName());
+
+    /** */
+    private static final String EVENT_NODE_REST = "node:rest";
+
+    /** */
+    private static final String EVENT_SCHEMA_IMPORT_DRIVERS = "schemaImport:drivers";
+
+    /** */
+    private static final String EVENT_SCHEMA_IMPORT_SCHEMAS = "schemaImport:schemas";
+
+    /** */
+    private static final String EVENT_SCHEMA_IMPORT_METADATA = "schemaImport:metadata";
+
+    /** */
+    private static final String EVENT_AGENT_WARNING = "agent:warning";
+
+    /** */
+    private static final String EVENT_AGENT_CLOSE = "agent:close";
+
+    /** */
+    private static final int RECONNECT_INTERVAL = 3000;
+
+    /**
+     * Create a trust manager that trusts all certificates It is not using a particular keyStore
+     */
+    private static TrustManager[] getTrustManagers() {
+        return new TrustManager[] {
+            new X509TrustManager() {
+                public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+
+                public void checkClientTrusted(
+                    java.security.cert.X509Certificate[] certs, String authType) {
+                }
+
+                public void checkServerTrusted(
+                    java.security.cert.X509Certificate[] certs, String authType) {
+                }
+            }};
+    }
+
+    /**
+     * On error listener.
+     */
+    private static final Emitter.Listener onError = new Emitter.Listener() {
+        @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+        @Override public void call(Object... args) {
+            Throwable e = (Throwable)args[0];
+
+            ConnectException ce = X.cause(e, ConnectException.class);
+
+            if (ce != null)
+                log.error("Failed to receive response from server (connection refused).");
+            else {
+                Exception ignore = X.cause(e, SSLHandshakeException.class);
+
+                if (ignore != null) {
+                    log.error("Failed to establish SSL connection to server, due to errors with SSL handshake.");
+                    log.error("Add to environment variable JVM_OPTS parameter \"-Dtrust.all=true\" to skip certificate validation in case of using self-signed certificate.");
+
+                    System.exit(1);
+                }
+
+                ignore = X.cause(e, IOException.class);
+
+                if (ignore != null && "404".equals(ignore.getMessage())) {
+                    log.error("Failed to receive response from server (connection refused).");
+
+                    return;
+                }
+
+                log.error("Connection error.", e);
+            }
+        }
+    };
+
+    /**
+     * On disconnect listener.
+     */
+    private static final Emitter.Listener onDisconnect = new Emitter.Listener() {
+        @Override public void call(Object... args) {
+            log.error(String.format("Connection closed: %s.", args));
+        }
+    };
+
+    /**
+     * @param args Args.
+     */
+    @SuppressWarnings("BusyWait")
+    public static void main(String[] args) throws Exception {
+        log.info("Starting Apache Ignite Web Console Agent...");
+
+        final AgentConfiguration cfg = new AgentConfiguration();
+
+        JCommander jCommander = new JCommander(cfg);
+
+        String osName = System.getProperty("os.name").toLowerCase();
+
+        jCommander.setProgramName("ignite-web-agent." + (osName.contains("win") ? "bat" : "sh"));
+
+        try {
+            jCommander.parse(args);
+        }
+        catch (ParameterException pe) {
+            log.error("Failed to parse command line parameters: " + Arrays.toString(args), pe);
+
+            jCommander.usage();
+
+            return;
+        }
+
+        String prop = cfg.configPath();
+
+        AgentConfiguration propCfg = new AgentConfiguration();
+
+        try {
+            File f = AgentUtils.resolvePath(prop);
+
+            if (f == null)
+                log.warn("Failed to find agent property file: " + prop);
+            else
+                propCfg.load(f.toURI().toURL());
+        }
+        catch (IOException ignore) {
+            if (!AgentConfiguration.DFLT_CFG_PATH.equals(prop))
+                log.warn("Failed to load agent property file: " + prop, ignore);
+        }
+
+        cfg.merge(propCfg);
+
+        if (cfg.help()) {
+            jCommander.usage();
+
+            return;
+        }
+
+        System.out.println();
+        System.out.println("Agent configuration:");
+        System.out.println(cfg);
+        System.out.println();
+
+        if (cfg.tokens() == null) {
+            String webHost;
+
+            try {
+                webHost = new URI(cfg.serverUri()).getHost();
+            }
+            catch (URISyntaxException e) {
+                log.error("Failed to parse Ignite Web Console uri", e);
+
+                return;
+            }
+
+            System.out.println("Security token is required to establish connection to the web console.");
+            System.out.println(String.format("It is available on the Profile page: https://%s/profile", webHost));
+
+            System.out.print("Enter security tokens separated by comma: ");
+
+            cfg.tokens(Arrays.asList(System.console().readLine().trim().split(",")));
+        }
+
+        final RestHandler restHnd = new RestHandler(cfg);
+
+        try {
+            restHnd.start();
+
+            URI uri = URI.create(cfg.serverUri());
+
+            if (uri.getPort() == -1)
+                uri = URI.create(cfg.serverUri() + ':' + DFLT_SERVER_PORT);
+
+            IO.Options opts = new IO.Options();
+
+            opts.reconnectionDelay = RECONNECT_INTERVAL;
+
+            // Workaround for use self-signed certificate
+            if (Boolean.getBoolean("trust.all")) {
+                SSLContext ctx = SSLContext.getInstance("TLS");
+
+                // Create an SSLContext that uses our TrustManager
+                ctx.init(null, getTrustManagers(), null);
+
+                opts.sslContext = ctx;
+            }
+
+            final Socket client = IO.socket(uri, opts);
+
+            try {
+                Emitter.Listener onConnecting = new Emitter.Listener() {
+                    @Override public void call(Object... args) {
+                        log.info("Connecting to: " + cfg.serverUri());
+                    }
+                };
+
+                Emitter.Listener onConnect = new Emitter.Listener() {
+                    @Override public void call(Object... args) {
+                        log.info("Connection established.");
+
+                        JSONObject authMsg = new JSONObject();
+
+                        try {
+                            authMsg.put("tokens", cfg.tokens());
+
+                            String clsName = AgentLauncher.class.getSimpleName() + ".class";
+
+                            String clsPath = AgentLauncher.class.getResource(clsName).toString();
+
+                            if (clsPath.startsWith("jar")) {
+                                String manifestPath = clsPath.substring(0, clsPath.lastIndexOf('!') + 1) +
+                                    "/META-INF/MANIFEST.MF";
+
+                                Manifest manifest = new Manifest(new URL(manifestPath).openStream());
+
+                                Attributes attr = manifest.getMainAttributes();
+
+                                authMsg.put("ver", attr.getValue("Implementation-Version"));
+                                authMsg.put("bt", attr.getValue("Build-Time"));
+                            }
+
+                            client.emit("agent:auth", authMsg, new Ack() {
+                                @Override public void call(Object... args) {
+                                    // Authentication failed if response contains args.
+                                    if (args != null && args.length > 0) {
+                                        onDisconnect.call(args);
+
+                                        System.exit(1);
+                                    }
+
+                                    log.info("Authentication success.");
+                                }
+                            });
+                        }
+                        catch (JSONException | IOException e) {
+                            log.error("Failed to construct authentication message", e);
+
+                            client.close();
+                        }
+                    }
+                };
+
+                DatabaseHandler dbHnd = new DatabaseHandler(cfg);
+
+                final CountDownLatch latch = new CountDownLatch(1);
+
+                client
+                    .on(EVENT_CONNECTING, onConnecting)
+                    .on(EVENT_CONNECT, onConnect)
+                    .on(EVENT_CONNECT_ERROR, onError)
+                    .on(EVENT_RECONNECTING, onConnecting)
+                    .on(EVENT_NODE_REST, restHnd)
+                    .on(EVENT_SCHEMA_IMPORT_DRIVERS, dbHnd.availableDriversListener())
+                    .on(EVENT_SCHEMA_IMPORT_SCHEMAS, dbHnd.schemasListener())
+                    .on(EVENT_SCHEMA_IMPORT_METADATA, dbHnd.metadataListener())
+                    .on(EVENT_ERROR, onError)
+                    .on(EVENT_DISCONNECT, onDisconnect)
+                    .on(EVENT_AGENT_WARNING, new Emitter.Listener() {
+                        @Override public void call(Object... args) {
+                            log.warn(args[0]);
+                        }
+                    })
+                    .on(EVENT_AGENT_CLOSE, new Emitter.Listener() {
+                        @Override public void call(Object... args) {
+                            onDisconnect.call(args);
+
+                            client.off();
+
+                            latch.countDown();
+                        }
+                    });
+
+                client.connect();
+
+                latch.await();
+            }
+            finally {
+                client.close();
+            }
+        }
+        finally {
+            restHnd.stop();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentUtils.java
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentUtils.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentUtils.java
new file mode 100644
index 0000000..50a849a
--- /dev/null
+++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/AgentUtils.java
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.console.agent;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.ProtectionDomain;
+import org.apache.log4j.Logger;
+
+/**
+ * Utility methods.
+ */
+public class AgentUtils {
+    /** */
+    private static final Logger log = Logger.getLogger(AgentUtils.class.getName());
+
+    /**
+     * Default constructor.
+     */
+    private AgentUtils() {
+        // No-op.
+    }
+
+    /**
+     * @param path Path to normalize.
+     * @return Normalized file path.
+     */
+    public static String normalizePath(String path) {
+        return path != null ? path.replace('\\', '/') : null;
+    }
+
+    /**
+     * @return App folder.
+     */
+    public static File getAgentHome() {
+        try {
+            ProtectionDomain domain = AgentLauncher.class.getProtectionDomain();
+
+            // Should not happen, but to make sure our code is not broken.
+            if (domain == null || domain.getCodeSource() == null || domain.getCodeSource().getLocation() == null) {
+                log.warn("Failed to resolve agent jar location!");
+
+                return null;
+            }
+
+            // Resolve path to class-file.
+            URI classesUri = domain.getCodeSource().getLocation().toURI();
+
+            boolean win = System.getProperty("os.name").toLowerCase().contains("win");
+
+            // Overcome UNC path problem on Windows (http://www.tomergabel.com/JavaMishandlesUNCPathsOnWindows.aspx)
+            if (win && classesUri.getAuthority() != null)
+                classesUri = new URI(classesUri.toString().replace("file://", "file:/"));
+
+            return new File(classesUri).getParentFile();
+        }
+        catch (URISyntaxException | SecurityException ignored) {
+            log.warn("Failed to resolve agent jar location!");
+
+            return null;
+        }
+    }
+
+    /**
+     * Gets file associated with path.
+     * <p>
+     * First check if path is relative to agent home.
+     * If not, check if path is absolute.
+     * If all checks fail, then {@code null} is returned.
+     * <p>
+     *
+     * @param path Path to resolve.
+     * @return Resolved path as file, or {@code null} if path cannot be resolved.
+     */
+    public static File resolvePath(String path) {
+        assert path != null;
+
+        File home = getAgentHome();
+
+        if (home != null) {
+            File file = new File(home, normalizePath(path));
+
+            if (file.exists())
+                return file;
+        }
+
+        // 2. Check given path as absolute.
+        File file = new File(path);
+
+        if (file.exists())
+            return file;
+
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractHandler.java
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractHandler.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractHandler.java
new file mode 100644
index 0000000..7e4e320
--- /dev/null
+++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/AbstractHandler.java
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.console.agent.handlers;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
+import io.socket.client.Ack;
+import io.socket.emitter.Emitter;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+/**
+ * Base class for web socket handlers.
+ */
+abstract class AbstractHandler implements Emitter.Listener {
+    /** JSON object mapper. */
+    private static final ObjectMapper mapper = new ObjectMapper();
+
+    static {
+        JsonOrgModule module = new JsonOrgModule();
+
+        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
+        mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
+
+        mapper.registerModule(module);
+    }
+
+    /**
+     * @param obj Object.
+     * @return {@link JSONObject} or {@link JSONArray}.
+     */
+    private Object toJSON(Object obj) {
+        if (obj instanceof Iterable)
+            return mapper.convertValue(obj, JSONArray.class);
+
+        return mapper.convertValue(obj, JSONObject.class);
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public final void call(Object... args) {
+        Ack cb = null;
+
+        try {
+            if (args == null || args.length == 0)
+                throw new IllegalArgumentException("Missing arguments.");
+
+            if (args.length > 2)
+                throw new IllegalArgumentException("Wrong arguments count, must be <= 2: " + Arrays.toString(args));
+
+            JSONObject lsnrArgs = null;
+
+            if (args.length == 1) {
+                if (args[0] instanceof JSONObject)
+                    lsnrArgs = (JSONObject)args[0];
+                else if (args[0] instanceof Ack)
+                    cb = (Ack)args[0];
+                else
+                    throw new IllegalArgumentException("Wrong type of argument, must be JSONObject or Ack: " + args[0]);
+            }
+            else {
+                if (args[0] != null && !(args[0] instanceof JSONObject))
+                    throw new IllegalArgumentException("Wrong type of argument, must be JSONObject: " + args[0]);
+
+                if (!(args[1] instanceof Ack))
+                    throw new IllegalArgumentException("Wrong type of argument, must be Ack: " + args[1]);
+
+                lsnrArgs = (JSONObject)args[0];
+
+                cb = (Ack)args[1];
+            }
+
+            Object res = execute(lsnrArgs == null ? Collections.emptyMap() : mapper.convertValue(lsnrArgs, Map.class));
+
+            if (cb != null)
+                cb.call(null, toJSON(res));
+        }
+        catch (Exception e) {
+            if (cb != null)
+                cb.call(e, null);
+        }
+    }
+
+    /**
+     * Execute command with specified arguments.
+     *
+     * @param args Map with method args.
+     */
+    public abstract Object execute(Map<String, Object> args) throws Exception;
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseHandler.java
----------------------------------------------------------------------
diff --git a/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseHandler.java b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseHandler.java
new file mode 100644
index 0000000..02146d9
--- /dev/null
+++ b/modules/web-console/web-agent/src/main/java/org/apache/ignite/console/agent/handlers/DatabaseHandler.java
@@ -0,0 +1,298 @@
+/*
+ * 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.
+ */
+
+package org.apache.ignite.console.agent.handlers;
+
+import io.socket.emitter.Emitter;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import org.apache.ignite.console.agent.AgentConfiguration;
+import org.apache.ignite.console.demo.AgentMetadataDemo;
+import org.apache.ignite.schema.parser.DbMetadataReader;
+import org.apache.ignite.schema.parser.DbTable;
+import org.apache.log4j.Logger;
+
+import static org.apache.ignite.console.agent.AgentUtils.resolvePath;
+
+/**
+ * API to extract database metadata.
+ */
+public class DatabaseHandler {
+    /** */
+    private static final Logger log = Logger.getLogger(DatabaseHandler.class.getName());
+
+    /** */
+    private final File driversFolder;
+
+    /**
+     * @param cfg Config.
+     */
+    public DatabaseHandler(AgentConfiguration cfg) {
+        driversFolder = resolvePath(cfg.driversFolder() == null ? "jdbc-drivers" : cfg.driversFolder());
+    }
+
+    /**
+     * @param jdbcDriverJarPath JDBC driver JAR path.
+     * @param jdbcDriverCls JDBC driver class.
+     * @param jdbcUrl JDBC URL.
+     * @param jdbcInfo Properties to connect to database.
+     * @return Connection to database.
+     * @throws SQLException
+     */
+    private Connection connect(String jdbcDriverJarPath, String jdbcDriverCls, String jdbcUrl,
+        Properties jdbcInfo) throws SQLException {
+        if (AgentMetadataDemo.isTestDriveUrl(jdbcUrl))
+            return AgentMetadataDemo.testDrive();
+
+        if (!new File(jdbcDriverJarPath).isAbsolute() && driversFolder != null)
+            jdbcDriverJarPath = new File(driversFolder, jdbcDriverJarPath).getPath();
+
+        return DbMetadataReader.getInstance().connect(jdbcDriverJarPath, jdbcDriverCls, jdbcUrl, jdbcInfo);
+    }
+
+    /**
+     * @param jdbcDriverJarPath JDBC driver JAR path.
+     * @param jdbcDriverCls JDBC driver class.
+     * @param jdbcUrl JDBC URL.
+     * @param jdbcInfo Properties to connect to database.
+     * @return Collection of schema names.
+     * @throws SQLException
+     */
+    protected Collection<String> schemas(String jdbcDriverJarPath, String jdbcDriverCls, String jdbcUrl,
+        Properties jdbcInfo) throws SQLException {
+        if (log.isDebugEnabled())
+            log.debug("Start collecting database schemas [drvJar=" + jdbcDriverJarPath +
+                ", drvCls=" + jdbcDriverCls + ", jdbcUrl=" + jdbcUrl + "]");
+
+        try (Connection conn = connect(jdbcDriverJarPath, jdbcDriverCls, jdbcUrl, jdbcInfo)) {
+            Collection<String> schemas = DbMetadataReader.getInstance().schemas(conn);
+
+            if (log.isDebugEnabled())
+                log.debug("Finished collection of schemas [jdbcUrl=" + jdbcUrl + ", count=" + schemas.size() + "]");
+
+            return schemas;
+        }
+        catch (Throwable e) {
+            log.error("Failed to collect schemas", e);
+
+            throw new SQLException("Failed to collect schemas", e);
+        }
+    }
+
+    /**
+     * Listener for schema names.
+     *
+     * @return Collection of schema names.
+     */
+    public Emitter.Listener schemasListener() {
+        return new AbstractHandler() {
+            @Override public Object execute(Map<String, Object> args) throws Exception {
+                String driverPath = null;
+
+                if (args.containsKey("driverPath"))
+                    driverPath = args.get("driverPath").toString();
+
+                if (!args.containsKey("driverClass"))
+                    throw new IllegalArgumentException("Missing driverClass in arguments: " + args);
+
+                String driverCls = args.get("driverClass").toString();
+
+                if (!args.containsKey("url"))
+                    throw new IllegalArgumentException("Missing url in arguments: " + args);
+
+                String url = args.get("url").toString();
+
+                if (!args.containsKey("info"))
+                    throw new IllegalArgumentException("Missing info in arguments: " + args);
+
+                Properties info = new Properties();
+
+                info.putAll((Map)args.get("info"));
+
+                return schemas(driverPath, driverCls, url, info);
+            }
+        };
+    }
+
+    /**
+     * @param jdbcDriverJarPath JDBC driver JAR path.
+     * @param jdbcDriverCls JDBC driver class.
+     * @param jdbcUrl JDBC URL.
+     * @param jdbcInfo Properties to connect to database.
+     * @param schemas List of schema names to process.
+     * @param tblsOnly If {@code true} then only tables will be processed otherwise views also will be processed.
+     * @return Collection of tables.
+     */
+    protected Collection<DbTable> metadata(String jdbcDriverJarPath, String jdbcDriverCls, String jdbcUrl,
+        Properties jdbcInfo, List<String> schemas, boolean tblsOnly) throws SQLException {
+        if (log.isDebugEnabled())
+            log.debug("Start collecting database metadata [drvJar=" + jdbcDriverJarPath +
+                ", drvCls=" + jdbcDriverCls + ", jdbcUrl=" + jdbcUrl + "]");
+
+        try (Connection conn = connect(jdbcDriverJarPath, jdbcDriverCls, jdbcUrl, jdbcInfo)) {
+            Collection<DbTable> metadata = DbMetadataReader.getInstance().metadata(conn, schemas, tblsOnly);
+
+            if (log.isDebugEnabled())
+                log.debug("Finished collection of metadata [jdbcUrl=" + jdbcUrl + ", count=" + metadata.size() + "]");
+
+            return metadata;
+        }
+        catch (Throwable e) {
+            log.error("Failed to collect metadata", e);
+
+            throw new SQLException("Failed to collect metadata", e);
+        }
+    }
+
+    /**
+     * Listener for tables.
+     *
+     * @return Collection of tables.
+     */
+    public Emitter.Listener metadataListener() {
+        return new AbstractHandler() {
+            @SuppressWarnings("unchecked")
+            @Override public Object execute(Map<String, Object> args) throws Exception {
+                String driverPath = null;
+
+                if (args.containsKey("driverPath"))
+                    driverPath = args.get("driverPath").toString();
+
+                if (!args.containsKey("driverClass"))
+                    throw new IllegalArgumentException("Missing driverClass in arguments: " + args);
+
+                String driverCls = args.get("driverClass").toString();
+
+                if (!args.containsKey("url"))
+                    throw new IllegalArgumentException("Missing url in arguments: " + args);
+
+                String url = args.get("url").toString();
+
+                if (!args.containsKey("info"))
+                    throw new IllegalArgumentException("Missing info in arguments: " + args);
+
+                Properties info = new Properties();
+
+                info.putAll((Map)args.get("info"));
+
+                if (!args.containsKey("schemas"))
+                    throw new IllegalArgumentException("Missing schemas in arguments: " + args);
+
+                List<String> schemas = (List<String>)args.get("schemas");
+
+                if (!args.containsKey("tablesOnly"))
+                    throw new IllegalArgumentException("Missing tablesOnly in arguments: " + args);
+
+                boolean tblsOnly = (boolean)args.get("tablesOnly");
+
+                return metadata(driverPath, driverCls, url, info, schemas, tblsOnly);
+            }
+        };
+    }
+
+    /**
+     * Listener for drivers.
+     *
+     * @return Drivers in drivers folder
+     * @see AgentConfiguration#driversFolder
+     */
+    public Emitter.Listener availableDriversListener() {
+        return new AbstractHandler() {
+            @Override public Object execute(Map<String, Object> args) throws Exception {
+                if (driversFolder == null) {
+                    log.info("JDBC drivers folder not specified, returning empty list");
+
+                    return Collections.emptyList();
+                }
+
+                if (log.isDebugEnabled())
+                    log.debug("Collecting JDBC drivers in folder: " + driversFolder.getPath());
+
+                File[] list = driversFolder.listFiles(new FilenameFilter() {
+                    @Override public boolean accept(File dir, String name) {
+                        return name.endsWith(".jar");
+                    }
+                });
+
+                if (list == null) {
+                    log.info("JDBC drivers folder has no files, returning empty list");
+
+                    return Collections.emptyList();
+                }
+
+                List<JdbcDriver> res = new ArrayList<>();
+
+                for (File file : list) {
+                    try {
+                        boolean win = System.getProperty("os.name").contains("win");
+
+                        URL url = new URL("jar", null,
+                            "file:" + (win ? "/" : "") + file.getPath() + "!/META-INF/services/java.sql.Driver");
+
+                        try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()))) {
+                            String jdbcDriverCls = reader.readLine();
+
+                            res.add(new JdbcDriver(file.getName(), jdbcDriverCls));
+
+                            if (log.isDebugEnabled())
+                                log.debug("Found: [driver=" + file + ", class=" + jdbcDriverCls + "]");
+                        }
+                    }
+                    catch (IOException e) {
+                        res.add(new JdbcDriver(file.getName(), null));
+
+                        log.info("Found: [driver=" + file + "]");
+                        log.info("Failed to detect driver class: " + e.getMessage());
+                    }
+                }
+
+                return res;
+            }
+        };
+    }
+
+    /**
+     * Wrapper class for later to be transformed to JSON and send to Web Console.
+     */
+    private static class JdbcDriver {
+        /** */
+        public final String jdbcDriverJar;
+        /** */
+        public final String jdbcDriverCls;
+
+        /**
+         * @param jdbcDriverJar File name of driver jar file.
+         * @param jdbcDriverCls Optional JDBC driver class.
+         */
+        public JdbcDriver(String jdbcDriverJar, String jdbcDriverCls) {
+            this.jdbcDriverJar = jdbcDriverJar;
+            this.jdbcDriverCls = jdbcDriverCls;
+        }
+    }
+}