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

ignite git commit: # IGNITE-1148 Added sql notebooks.

Repository: ignite
Updated Branches:
  refs/heads/ignite-843 0b72d82a6 -> ebca5fa00


# IGNITE-1148 Added sql notebooks.


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

Branch: refs/heads/ignite-843
Commit: ebca5fa001dcb565dbf585fdb8c0816ac4dd9a79
Parents: 0b72d82
Author: Andrey <an...@gridgain.com>
Authored: Tue Aug 25 14:30:18 2015 +0700
Committer: Andrey <an...@gridgain.com>
Committed: Tue Aug 25 14:30:18 2015 +0700

----------------------------------------------------------------------
 .../assembly/release-control-center-agent.xml   |   7 +
 modules/control-center-agent/pom.xml            |  14 +-
 .../apache/ignite/agent/AgentConfiguration.java |   2 +-
 .../org/apache/ignite/agent/AgentLauncher.java  |   9 +-
 .../org/apache/ignite/agent/AgentSocket.java    |  20 +-
 .../ignite/agent/handlers/RestExecutor.java     |   6 +-
 .../ignite/agent/remote/RemoteHandler.java      |   7 +
 .../agent/testdrive/AgentMetadataTestDrive.java |  35 +--
 .../agent/testdrive/AgentSqlTestDrive.java      | 250 ++++++++++++++++++-
 .../test-drive/test-drive.sql                   |  65 ++---
 .../src/main/js/controllers/sql-controller.js   |  21 +-
 modules/control-center-web/src/main/js/db.js    |   8 +-
 .../src/main/js/routes/notebooks.js             |   2 +-
 .../src/main/js/views/sql/sql.jade              |   6 +-
 .../http/jetty/GridJettyJsonConfig.java         |  48 +++-
 15 files changed, 423 insertions(+), 77 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/control-center-agent/assembly/release-control-center-agent.xml
----------------------------------------------------------------------
diff --git a/modules/control-center-agent/assembly/release-control-center-agent.xml b/modules/control-center-agent/assembly/release-control-center-agent.xml
index e7f46e8..29e548e 100644
--- a/modules/control-center-agent/assembly/release-control-center-agent.xml
+++ b/modules/control-center-agent/assembly/release-control-center-agent.xml
@@ -28,6 +28,13 @@
 
     <fileSets>
         <fileSet>
+            <directory>${basedir}/../indexing/target/libs</directory>
+            <outputDirectory>/jdbc-drivers</outputDirectory>
+            <includes>
+                <include>**/h2-*.jar</include>
+            </includes>
+        </fileSet>
+        <fileSet>
             <directory>${basedir}</directory>
             <outputDirectory>/</outputDirectory>
             <includes>

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/control-center-agent/pom.xml
----------------------------------------------------------------------
diff --git a/modules/control-center-agent/pom.xml b/modules/control-center-agent/pom.xml
index 2d5123e..72fb46c 100644
--- a/modules/control-center-agent/pom.xml
+++ b/modules/control-center-agent/pom.xml
@@ -59,7 +59,7 @@
         <dependency>
             <groupId>com.beust</groupId>
             <artifactId>jcommander</artifactId>
-            <version>1.32</version>
+            <version>1.48</version>
         </dependency>
 
         <dependency>
@@ -69,9 +69,15 @@
         </dependency>
 
         <dependency>
-            <groupId>com.h2database</groupId>
-            <artifactId>h2</artifactId>
-            <version>1.4.188</version>
+            <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>
     </dependencies>
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/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 d156509..44a7f1f 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
@@ -72,7 +72,7 @@ public class AgentConfiguration {
     private Boolean sql;
 
     /** */
-    @Parameter(names = { "-h", "--help" }, description = "Print this help message")
+    @Parameter(names = { "-h", "--help" }, help = true, description = "Print this help message")
     private Boolean help;
 
     /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentLauncher.java
----------------------------------------------------------------------
diff --git a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentLauncher.java b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentLauncher.java
index f2d8cab..f04d9d9 100644
--- a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentLauncher.java
+++ b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/AgentLauncher.java
@@ -45,6 +45,7 @@ public class AgentLauncher {
     /**
      * @param args Args.
      */
+    @SuppressWarnings("BusyWait")
     public static void main(String[] args) throws Exception {
         log.log(Level.INFO, "Starting Apache Ignite Control Center Agent...");
 
@@ -77,12 +78,6 @@ public class AgentLauncher {
             cfg.password(new String(System.console().readPassword()));
         }
 
-        if (cfg.testDriveMetadata())
-            AgentMetadataTestDrive.testDrive();
-
-        if (cfg.testDriveSql())
-            AgentSqlTestDrive.testDrive();
-
         RestExecutor restExecutor = new RestExecutor(cfg);
 
         restExecutor.start();
@@ -91,7 +86,7 @@ public class AgentLauncher {
             SslContextFactory sslCtxFactory = new SslContextFactory();
 
             // TODO IGNITE-843 Fix issue with trust all: if (Boolean.TRUE.equals(Boolean.getBoolean("trust.all")))
-                sslCtxFactory.setTrustAll(true);
+            sslCtxFactory.setTrustAll(true);
 
             WebSocketClient client = new WebSocketClient(sslCtxFactory);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/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 39a3ade..b329967 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
@@ -18,8 +18,10 @@
 package org.apache.ignite.agent;
 
 import com.google.gson.*;
+import org.apache.http.auth.*;
 import org.apache.ignite.agent.handlers.*;
 import org.apache.ignite.agent.remote.*;
+import org.apache.ignite.agent.testdrive.*;
 import org.eclipse.jetty.websocket.api.*;
 import org.eclipse.jetty.websocket.api.annotations.*;
 
@@ -156,17 +158,23 @@ public class AgentSocket implements WebSocketSender {
      */
     @Remote
     public void authResult(String errorMsg) {
-        if (errorMsg == null)
-            log.info("Authentication success.");
-        else {
-            log.info("Authentication failed: " + errorMsg);
+        if (errorMsg != null) {
+            onClose(401, "Authentication failed: " + errorMsg);
 
-            ses.close();
+            System.exit(1);
         }
+
+        log.info("Authentication success.");
+
+        if (cfg.testDriveMetadata())
+            AgentMetadataTestDrive.testDrive();
+
+        if (cfg.testDriveSql())
+            AgentSqlTestDrive.testDrive();
     }
 
     /**
-     *
+     * Await socket close.
      */
     public void waitForClose() throws InterruptedException {
         closeLatch.await();

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/control-center-agent/src/main/java/org/apache/ignite/agent/handlers/RestExecutor.java
----------------------------------------------------------------------
diff --git a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/handlers/RestExecutor.java b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/handlers/RestExecutor.java
index c86ae16..98fd2fa 100644
--- a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/handlers/RestExecutor.java
+++ b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/handlers/RestExecutor.java
@@ -141,13 +141,13 @@ public class RestExecutor {
     }
 
     /**
-     *
+     * Request result.
      */
     public static class RestResult {
-        /** */
+        /** Status code. */
         private int code;
 
-        /** */
+        /** Message. */
         private String message;
 
         /**

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/control-center-agent/src/main/java/org/apache/ignite/agent/remote/RemoteHandler.java
----------------------------------------------------------------------
diff --git a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/remote/RemoteHandler.java b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/remote/RemoteHandler.java
index e970470..5ca6379 100644
--- a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/remote/RemoteHandler.java
+++ b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/remote/RemoteHandler.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.agent.remote;
 
 import com.google.gson.*;
+import org.apache.http.auth.*;
 
 import java.lang.reflect.*;
 import java.util.*;
@@ -128,6 +129,12 @@ public class RemoteHandler implements AutoCloseable {
                     res = desc.mtd.invoke(desc.hnd, args);
                 }
                 catch (Throwable e) {
+                    if (e instanceof AuthenticationException) {
+                        close();
+
+                        return;
+                    }
+
                     if (e instanceof InvocationTargetException)
                         e = ((InvocationTargetException)e).getTargetException();
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/control-center-agent/src/main/java/org/apache/ignite/agent/testdrive/AgentMetadataTestDrive.java
----------------------------------------------------------------------
diff --git a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/testdrive/AgentMetadataTestDrive.java b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/testdrive/AgentMetadataTestDrive.java
index ecb08af..6d2ab50 100644
--- a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/testdrive/AgentMetadataTestDrive.java
+++ b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/testdrive/AgentMetadataTestDrive.java
@@ -5,6 +5,7 @@ import org.h2.tools.*;
 
 import java.io.*;
 import java.sql.*;
+import java.util.concurrent.atomic.*;
 import java.util.logging.*;
 
 /**
@@ -16,6 +17,9 @@ public class AgentMetadataTestDrive {
     /** */
     private static final Logger log = Logger.getLogger(AgentMetadataTestDrive.class.getName());
 
+    /** */
+    private static final AtomicBoolean initLatch = new AtomicBoolean();
+
     /**
      * Execute query.
      *
@@ -32,28 +36,31 @@ public class AgentMetadataTestDrive {
      * Start H2 database and populate it with several tables.
      */
     public static void testDrive() {
-        log.log(Level.INFO, "TEST-DRIVE: Prepare in-memory H2 database...");
+        if (initLatch.compareAndSet(false, true)) {
+            log.log(Level.INFO, "TEST-DRIVE: Prepare in-memory H2 database...");
 
-        try {
-            Connection conn = DriverManager.getConnection("jdbc:h2:mem:test-drive-db;DB_CLOSE_DELAY=-1", "sa", "");
+            try {
+                Connection conn = DriverManager.getConnection("jdbc:h2:mem:test-drive-db;DB_CLOSE_DELAY=-1", "sa", "");
 
-            File agentHome = AgentUtils.getAgentHome();
+                File agentHome = AgentUtils.getAgentHome();
 
-            File sqlScript = new File((agentHome != null) ? new File(agentHome, "test-drive") : new File("test-drive"),
-                "test-drive.sql");
+                File sqlScript = new File((agentHome != null) ? new File(agentHome, "test-drive") : new File("test-drive"),
+                    "test-drive.sql");
 
-            RunScript.execute(conn, new FileReader(sqlScript));
-            log.log(Level.INFO, "TEST-DRIVE: Sample tables created.");
+                RunScript.execute(conn, new FileReader(sqlScript));
+                log.log(Level.INFO, "TEST-DRIVE: Sample tables created.");
 
-            conn.close();
+                conn.close();
 
-            Server.createTcpServer("-tcpDaemon").start();
+                Server.createTcpServer("-tcpDaemon").start();
 
-            log.log(Level.INFO, "TEST-DRIVE: TcpServer stared.");
+                log.log(Level.INFO, "TEST-DRIVE: TcpServer stared.");
 
-            log.log(Level.INFO, "TEST-DRIVE: JDBC URL for test drive metadata load: jdbc:h2:mem:test-drive-db");
-        } catch (Exception e) {
-            log.log(Level.SEVERE, "TEST-DRIVE: Failed to start test drive for metadata!", e);
+                log.log(Level.INFO, "TEST-DRIVE: JDBC URL for test drive metadata load: jdbc:h2:mem:test-drive-db");
+            }
+            catch (Exception e) {
+                log.log(Level.SEVERE, "TEST-DRIVE: Failed to start test drive for metadata!", e);
+            }
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/control-center-agent/src/main/java/org/apache/ignite/agent/testdrive/AgentSqlTestDrive.java
----------------------------------------------------------------------
diff --git a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/testdrive/AgentSqlTestDrive.java b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/testdrive/AgentSqlTestDrive.java
index 9c5c7e5..2e7a4c8 100644
--- a/modules/control-center-agent/src/main/java/org/apache/ignite/agent/testdrive/AgentSqlTestDrive.java
+++ b/modules/control-center-agent/src/main/java/org/apache/ignite/agent/testdrive/AgentSqlTestDrive.java
@@ -1,15 +1,263 @@
 package org.apache.ignite.agent.testdrive;
 
+import org.apache.ignite.*;
+import org.apache.ignite.agent.testdrive.model.*;
+import org.apache.ignite.cache.*;
+import org.apache.ignite.configuration.*;
+import org.apache.ignite.lang.*;
+
+import java.util.*;
+import java.util.concurrent.atomic.*;
+import java.util.logging.*;
+
 /**
  * Test drive for SQL.
  *
  * Cache will be created and populated with data to query.
  */
 public class AgentSqlTestDrive {
+    /** */
+    private static final Logger log = Logger.getLogger(AgentMetadataTestDrive.class.getName());
+
+    /** */
+    private static final AtomicBoolean initLatch = new AtomicBoolean();
+
+    private static final String CACHE_NAME = "test-drive-sql";
+
+    private static final Random rnd = new Random();
+
+    /** Countries count. */
+    private static final int CNTR_CNT = 10;
+
+    /** Departments count */
+    private static final int DEP_CNT = 100;
+
+    /** Employees count. */
+    private static final int EMPL_CNT = 1000;
+
     /**
-     * TODO IGNITE-843
+     * Configure cache.
+     *
+     * @param name Cache name.
+     */
+    private static <K, V> CacheConfiguration<K, V> cache(String name) {
+        CacheConfiguration<K, V> ccfg = new CacheConfiguration<>(name);
+
+        // Configure cache types.
+        Collection<CacheTypeMetadata> meta = new ArrayList<>();
+
+        // CAR.
+        CacheTypeMetadata type = new CacheTypeMetadata();
+
+        meta.add(type);
+
+        type.setKeyType(CarKey.class.getName());
+        type.setValueType(Car.class.getName());
+
+        // Query fields for CAR.
+        Map<String, Class<?>> qryFlds = new LinkedHashMap<>();
+
+        qryFlds.put("carId", int.class);
+        qryFlds.put("parkingId", int.class);
+        qryFlds.put("carName", String.class);
+
+        type.setQueryFields(qryFlds);
+
+        // Ascending fields for CAR.
+        Map<String, Class<?>> ascFlds = new LinkedHashMap<>();
+
+        ascFlds.put("carId", int.class);
+
+        type.setAscendingFields(ascFlds);
+
+        ccfg.setTypeMetadata(meta);
+
+        // PARKING.
+        type = new CacheTypeMetadata();
+
+        meta.add(type);
+
+        type.setKeyType(ParkingKey.class.getName());
+        type.setValueType(Parking.class.getName());
+
+        // Query fields for PARKING.
+        qryFlds = new LinkedHashMap<>();
+
+        qryFlds.put("parkingId", int.class);
+        qryFlds.put("parkingName", String.class);
+
+        type.setQueryFields(qryFlds);
+
+        // Ascending fields for PARKING.
+        ascFlds = new LinkedHashMap<>();
+
+        ascFlds.put("parkingId", int.class);
+
+        type.setAscendingFields(ascFlds);
+
+        ccfg.setTypeMetadata(meta);
+
+        // COUNTRY.
+        type = new CacheTypeMetadata();
+
+        meta.add(type);
+
+        type.setKeyType(CountryKey.class.getName());
+        type.setValueType(Country.class.getName());
+
+        // Query fields for COUNTRY.
+        qryFlds = new LinkedHashMap<>();
+
+        qryFlds.put("id", int.class);
+        qryFlds.put("countryName", String.class);
+
+        type.setQueryFields(qryFlds);
+
+        // Ascending fields for COUNTRY.
+        ascFlds = new LinkedHashMap<>();
+
+        ascFlds.put("id", int.class);
+
+        type.setAscendingFields(ascFlds);
+
+        ccfg.setTypeMetadata(meta);
+
+        // DEPARTMENT.
+        type = new CacheTypeMetadata();
+
+        meta.add(type);
+
+        type.setKeyType(DepartmentKey.class.getName());
+        type.setValueType(Department.class.getName());
+
+        // Query fields for DEPARTMENT.
+        qryFlds = new LinkedHashMap<>();
+
+        qryFlds.put("departmentId", int.class);
+        qryFlds.put("departmentName", String.class);
+        qryFlds.put("countryId", Integer.class);
+        qryFlds.put("managerId", Integer.class);
+
+        type.setQueryFields(qryFlds);
+
+        // Ascending fields for DEPARTMENT.
+        ascFlds = new LinkedHashMap<>();
+
+        ascFlds.put("departmentId", int.class);
+
+        type.setAscendingFields(ascFlds);
+
+        ccfg.setTypeMetadata(meta);
+
+        // EMPLOYEE.
+        type = new CacheTypeMetadata();
+
+        meta.add(type);
+
+        type.setKeyType(EmployeeKey.class.getName());
+        type.setValueType(Employee.class.getName());
+
+        // Query fields for EMPLOYEE.
+        qryFlds = new LinkedHashMap<>();
+
+        qryFlds.put("employeeId", int.class);
+        qryFlds.put("firstName", String.class);
+        qryFlds.put("lastName", String.class);
+        qryFlds.put("email", String.class);
+        qryFlds.put("phoneNumber", String.class);
+        qryFlds.put("hireDate", java.sql.Date.class);
+        qryFlds.put("job", String.class);
+        qryFlds.put("salary", Double.class);
+        qryFlds.put("managerId", Integer.class);
+        qryFlds.put("departmentId", Integer.class);
+
+        type.setQueryFields(qryFlds);
+
+        // Ascending fields for EMPLOYEE.
+        ascFlds = new LinkedHashMap<>();
+
+        ascFlds.put("employeeId", int.class);
+        ascFlds.put("salary", Double.class);
+
+        type.setAscendingFields(ascFlds);
+
+        // Groups for EMPLOYEE.
+        Map<String, LinkedHashMap<String, IgniteBiTuple<Class<?>, Boolean>>> grps = new LinkedHashMap<>();
+
+        LinkedHashMap<String, IgniteBiTuple<Class<?>, Boolean>> grpItems = new LinkedHashMap<>();
+
+        grpItems.put("firstName", new IgniteBiTuple<Class<?>, Boolean>(String.class, false));
+        grpItems.put("lastName", new IgniteBiTuple<Class<?>, Boolean>(String.class, false));
+
+        grps.put("EMP_NAMES", grpItems);
+
+        type.setGroups(grps);
+
+        ccfg.setTypeMetadata(meta);
+
+        return ccfg;
+    }
+
+    /**
+     * @param ignite Ignite.
+     * @param name Cache name.
+     */
+    private static void populateCache(Ignite ignite, String name) {
+        log.log(Level.INFO, "TEST-DRIVE: Start population '" + name + "' cache with data...");
+
+        IgniteCache<CountryKey, Country> cacheCountry = ignite.cache(name);
+
+        for (int i = 0; i < CNTR_CNT; i++)
+            cacheCountry.put(new CountryKey(i), new Country(i, "State " + (i + 1)));
+
+        IgniteCache<DepartmentKey, Department> cacheDepartment = ignite.cache(name);
+
+        for (int i = 0; i < DEP_CNT; i++) {
+            Integer managerId = (i == 0 || rnd.nextBoolean()) ? null : rnd.nextInt(i);
+
+            cacheDepartment.put(new DepartmentKey(i),
+                new Department(i, "Department " + (i + 1), rnd.nextInt(CNTR_CNT), managerId));
+        }
+
+        IgniteCache<EmployeeKey, Employee> cacheEmployee = ignite.cache(name);
+
+        for (int i = 0; i < EMPL_CNT; i++) {
+            Integer managerId = (i == 0 || rnd.nextBoolean()) ? null : rnd.nextInt(i);
+
+            cacheEmployee.put(new EmployeeKey(i),
+                new Employee(i, "first name " + (i + 1), "last name " + (i + 1), "email " + (i + 1),
+                    "phone number " + (i + 1), new java.sql.Date(rnd.nextLong()), "job " + (i + 1),
+                    rnd.nextDouble() * 5000, managerId, rnd.nextInt(DEP_CNT)));
+        }
+
+        log.log(Level.INFO, "TEST-DRIVE: Finished population '" + name + "' cache with data.");
+    }
+
+    /**
+     * Start ignite node with cache and populate it with data.
      */
     public static void testDrive() {
+        if (initLatch.compareAndSet(false, true)) {
+            log.log(Level.INFO, "TEST-DRIVE: Prepare node configuration...");
+
+            try {
+                IgniteConfiguration cfg = new IgniteConfiguration();
+
+                cfg.setMetricsLogFrequency(0);
+
+                cfg.setCacheConfiguration(cache(CACHE_NAME));
+
+                log.log(Level.INFO, "TEST-DRIVE: Start embedded node with indexed enabled cache...");
+
+                Ignite ignite = Ignition.start(cfg);
+
+                log.log(Level.INFO, "TEST-DRIVE: Embedded node started");
 
+                populateCache(ignite, CACHE_NAME);
+            }
+            catch (Exception e) {
+                log.log(Level.SEVERE, "TEST-DRIVE: Failed to start test drive for sql!", e);
+            }
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/control-center-agent/test-drive/test-drive.sql
----------------------------------------------------------------------
diff --git a/modules/control-center-agent/test-drive/test-drive.sql b/modules/control-center-agent/test-drive/test-drive.sql
index fd910fe..1438204 100644
--- a/modules/control-center-agent/test-drive/test-drive.sql
+++ b/modules/control-center-agent/test-drive/test-drive.sql
@@ -15,41 +15,46 @@
  * limitations under the License.
  */
 
-CREATE TABLE COUNTRY(ID INTEGER NOT NULL PRIMARY KEY, COUNTRY_NAME VARCHAR(100));
-
-CREATE TABLE DEPARTMENT(
- DEPARTMENT_ID INTEGER  NOT NULL PRIMARY KEY,
- DEPARTMENT_NAME VARCHAR(50) NOT NULL,
- COUNTRY_ID INTEGER,
- MANAGER_ID INTEGER);
-
-CREATE TABLE EMPLOYEE(
- EMPLOYEE_ID INTEGER NOT NULL PRIMARY KEY,
- FIRST_NAME VARCHAR(20) NOT NULL,
- LAST_NAME VARCHAR(30) NOT NULL,
- EMAIL VARCHAR(25) NOT NULL,
- PHONE_NUMBER VARCHAR(20),
- HIRE_DATE DATE NOT NULL,
- JOB VARCHAR(50) NOT NULL,
- SALARY DOUBLE,
- MANAGER_ID INTEGER,
- DEPARTMENT_ID INTEGER);
-
-CREATE INDEX EMP_SALARY_A ON EMPLOYEE(SALARY ASC);
-CREATE INDEX EMP_SALARY_B ON EMPLOYEE(SALARY DESC);
-CREATE INDEX EMP_NAMES ON EMPLOYEE(FIRST_NAME ASC, LAST_NAME  ASC);
+CREATE TABLE COUNTRY (
+    ID           INTEGER NOT NULL PRIMARY KEY,
+    COUNTRY_NAME VARCHAR(100)
+);
+
+CREATE TABLE DEPARTMENT (
+    DEPARTMENT_ID   INTEGER     NOT NULL PRIMARY KEY,
+    DEPARTMENT_NAME VARCHAR(50) NOT NULL,
+    COUNTRY_ID      INTEGER,
+    MANAGER_ID      INTEGER
+);
+
+CREATE TABLE EMPLOYEE (
+    EMPLOYEE_ID   INTEGER     NOT NULL PRIMARY KEY,
+    FIRST_NAME    VARCHAR(20) NOT NULL,
+    LAST_NAME     VARCHAR(30) NOT NULL,
+    EMAIL         VARCHAR(25) NOT NULL,
+    PHONE_NUMBER  VARCHAR(20),
+    HIRE_DATE     DATE        NOT NULL,
+    JOB           VARCHAR(50) NOT NULL,
+    SALARY        DOUBLE,
+    MANAGER_ID    INTEGER,
+    DEPARTMENT_ID INTEGER
+);
+
+CREATE INDEX EMP_SALARY_A ON EMPLOYEE (SALARY ASC);
+CREATE INDEX EMP_SALARY_B ON EMPLOYEE (SALARY DESC);
+CREATE INDEX EMP_NAMES ON EMPLOYEE (FIRST_NAME ASC, LAST_NAME ASC);
 
 CREATE SCHEMA CARS;
 
-CREATE TABLE CARS.PARKING(
- PARKING_ID INTEGER NOT NULL PRIMARY KEY,
- PARKING_NAME VARCHAR(50) NOT NULL
+CREATE TABLE CARS.PARKING (
+    PARKING_ID   INTEGER     NOT NULL PRIMARY KEY,
+    PARKING_NAME VARCHAR(50) NOT NULL
 );
 
-CREATE TABLE CARS.CAR(
- CAR_ID INTEGER NOT NULL PRIMARY KEY,
- PARKING_ID INTEGER NOT NULL,
- CAR_NAME VARCHAR(50) NOT NULL
+CREATE TABLE CARS.CAR (
+    CAR_ID     INTEGER     NOT NULL PRIMARY KEY,
+    PARKING_ID INTEGER     NOT NULL,
+    CAR_NAME   VARCHAR(50) NOT NULL
 );
 
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/control-center-web/src/main/js/controllers/sql-controller.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/controllers/sql-controller.js b/modules/control-center-web/src/main/js/controllers/sql-controller.js
index 7a1eb49..5d37069 100644
--- a/modules/control-center-web/src/main/js/controllers/sql-controller.js
+++ b/modules/control-center-web/src/main/js/controllers/sql-controller.js
@@ -135,6 +135,15 @@ controlCenterModule.controller('sqlController', ['$scope', '$controller', '$http
                 $common.showError('Receive agent error: ' + err);
         });
 
+    var _appendOnLast = function(item) {
+        var idx = _.findIndex($scope.notebook.paragraphs, function (paragraph) {
+            return paragraph == item;
+        });
+
+        if ($scope.notebook.paragraphs.length == (idx + 1))
+            $scope.addParagraph();
+    };
+
     var _processQueryResult = function(item) {
         return function(res) {
             item.meta = [];
@@ -155,6 +164,8 @@ controlCenterModule.controller('sqlController', ['$scope', '$controller', '$http
     };
 
     $scope.execute = function(item) {
+        _appendOnLast(item);
+
         $http.post('/agent/query', {query: item.query, pageSize: item.pageSize, cacheName: item.cache.name})
             .success(_processQueryResult(item))
             .error(function (errMsg) {
@@ -163,14 +174,18 @@ controlCenterModule.controller('sqlController', ['$scope', '$controller', '$http
     };
 
     $scope.explain = function(item) {
+        _appendOnLast(item);
+
         $http.post('/agent/query', {query: 'EXPLAIN ' + item.query, pageSize: item.pageSize, cacheName: item.cache.name})
-            .success(_processQueryResult)
+            .success(_processQueryResult(item))
             .error(function (errMsg) {
                 $common.showError(errMsg);
             });
     };
 
     $scope.scan = function(item) {
+        _appendOnLast(item);
+
         $http.post('/agent/scan', {pageSize: item.pageSize, cacheName: item.cache.name})
             .success(_processQueryResult(item))
             .error(function (errMsg) {
@@ -195,6 +210,10 @@ controlCenterModule.controller('sqlController', ['$scope', '$controller', '$http
             });
     };
 
+    $scope.resultMode = function(paragraph, type) {
+        return (paragraph.result === type);
+    };
+
     $scope.getter = function (value) {
         return value;
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/control-center-web/src/main/js/db.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/db.js b/modules/control-center-web/src/main/js/db.js
index 6746758..06ba915 100644
--- a/modules/control-center-web/src/main/js/db.js
+++ b/modules/control-center-web/src/main/js/db.js
@@ -38,7 +38,7 @@ var AccountSchema = new Schema({
 });
 
 // Install passport plugin.
-AccountSchema.plugin(passportLocalMongoose, {usernameField: 'email', limitAttempts: true, lastLoginField: 'lastLogin',
+AccountSchema.plugin(passportLocalMongoose, {usernameField: 'email', limitAttempts: false, lastLoginField: 'lastLogin',
     usernameLowerCase: true});
 
 // Configure transformation to JSON.
@@ -338,8 +338,10 @@ exports.Cluster = mongoose.model('Cluster', ClusterSchema);
 var NotebookSchema = new Schema({
     space: {type: ObjectId, ref: 'Space'},
     name: String,
-    paragraph: [{
-        query: String
+    paragraphs: [{
+        name: String,
+        query: String,
+        result: {type: String, enum: ['table', 'bar']}
     }]
 });
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/control-center-web/src/main/js/routes/notebooks.js
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/routes/notebooks.js b/modules/control-center-web/src/main/js/routes/notebooks.js
index e891a1c..f929821 100644
--- a/modules/control-center-web/src/main/js/routes/notebooks.js
+++ b/modules/control-center-web/src/main/js/routes/notebooks.js
@@ -126,7 +126,7 @@ router.get('/new', function (req, res) {
 
         var name = 'Notebook' + ' ' + utils.randomValueHex(8);
 
-        (new db.Notebook({space: space.id, name: name, paragraph: []})).save(function (err, notebook) {
+        (new db.Notebook({space: space.id, name: name, paragraphs: []})).save(function (err, notebook) {
             if (err)
                 return res.status(500).send(err.message);
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/control-center-web/src/main/js/views/sql/sql.jade
----------------------------------------------------------------------
diff --git a/modules/control-center-web/src/main/js/views/sql/sql.jade b/modules/control-center-web/src/main/js/views/sql/sql.jade
index e49e3ad..c10d75c 100644
--- a/modules/control-center-web/src/main/js/views/sql/sql.jade
+++ b/modules/control-center-web/src/main/js/views/sql/sql.jade
@@ -43,8 +43,8 @@ block container
                                     a(bs-collapse-toggle ng-click='hidePopover()') {{paragraph.name}}
                                     i.fa.fa-pencil.tipLabel(ng-click='paragraph.edit = true; paragraph.edit_name = paragraph.name;' bs-tooltip data-title='Rename paragraph' data-trigger='hover')
                                     .result.btn-group(ng-model='paragraph.result')
-                                        i.btn.btn-default.fa.fa-table(ng-click='paragraph.result="table"' bs-tooltip data-title='Show table' data-trigger='hover')
-                                        i.btn.btn-default.fa.fa-bar-chart(ng-click='paragraph.result="bar"' bs-tooltip data-title='Show bar chart' data-trigger='hover')
+                                        i.btn.btn-default.fa.fa-table(ng-click='paragraph.result="table"' ng-class="{'active': resultMode(paragraph, 'table')}" bs-tooltip data-title='Show table' data-trigger='hover')
+                                        i.btn.btn-default.fa.fa-bar-chart(ng-click='paragraph.result="bar"' ng-class="{'active': resultMode(paragraph, 'bar')}" bs-tooltip data-title='Show bar chart' data-trigger='hover')
                                 h3(ng-show='paragraph.edit')
                                     input.sql-name-input(ng-model='paragraph.edit_name' on-enter='renameParagraph(paragraph, paragraph.edit_name)' on-escape='paragraph.edit = false;')
                                     i.tipLabel.fa.fa-floppy-o(ng-click='renameParagraph(paragraph, paragraph.edit_name)' bs-tooltip data-title='Save paragraph name' data-trigger='hover')
@@ -73,7 +73,7 @@ block container
                                         button.btn.btn-primary(ng-click='explain(paragraph)') Explain
                                         button.btn.btn-primary(ng-click='execute(paragraph)') Execute
                                         button.btn.btn-primary(ng-click='scan(paragraph)') Scan
-                                .panel-body(ng-show='paragraph.result == "table')
+                                .panel-body(ng-show='paragraph.result === "table"')
                                     table.table.table-striped.col-sm-12.sql-results(st-table='displayedCollection' st-safe-src='paragraph.rows')
                                         thead
                                             tr(style='border-size: 0')

http://git-wip-us.apache.org/repos/asf/ignite/blob/ebca5fa0/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java
----------------------------------------------------------------------
diff --git a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java
index ad6ee22..8d00249 100644
--- a/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java
+++ b/modules/rest-http/src/main/java/org/apache/ignite/internal/processors/rest/protocols/http/jetty/GridJettyJsonConfig.java
@@ -30,18 +30,31 @@ public class GridJettyJsonConfig extends JsonConfig {
      * Constructs default jetty json config.
      */
     public GridJettyJsonConfig() {
-        registerJsonValueProcessor(UUID.class, new ToStringJsonProcessor());
+        registerJsonValueProcessor(UUID.class, new UUIDToStringJsonProcessor());
+        registerJsonValueProcessor(Date.class, new DateToStringJsonProcessor());
+        registerJsonValueProcessor(java.sql.Date.class, new DateToStringJsonProcessor());
     }
 
     /**
      * Helper class for simple to-string conversion for the beans.
      */
-    private static class ToStringJsonProcessor implements JsonValueProcessor {
+    private static class UUIDToStringJsonProcessor implements JsonValueProcessor {
         /** {@inheritDoc} */
         @Override public Object processArrayValue(Object val, JsonConfig jsonCfg) {
-            if (val != null && val.getClass() == UUID.class)
+            if (val instanceof UUID)
                 return val.toString();
 
+            if (val instanceof UUID[]) {
+                UUID[] uuids = (UUID[])val;
+
+                String[] result = new String[uuids.length];
+
+                for (int i = 0; i < uuids.length; i++)
+                    result[i] = uuids[i] == null ? null : uuids[i].toString();
+
+                return result;
+            }
+
             throw new UnsupportedOperationException("Serialize array to string is not supported: " + val);
         }
 
@@ -50,4 +63,33 @@ public class GridJettyJsonConfig extends JsonConfig {
             return val == null ? null : val.toString();
         }
     }
+
+    /**
+     * Helper class for simple to-string conversion for the beans.
+     */
+    private static class DateToStringJsonProcessor implements JsonValueProcessor {
+        /** {@inheritDoc} */
+        @Override public Object processArrayValue(Object val, JsonConfig jsonCfg) {
+            if (val instanceof Date)
+                return val.toString();
+
+            if (val instanceof Date[]) {
+                Date[] dates = (Date[])val;
+
+                String[] result = new String[dates.length];
+
+                for (int i = 0; i < dates.length; i++)
+                    result[i] = dates[i] == null ? null : dates[i].toString();
+
+                return result;
+            }
+
+            throw new UnsupportedOperationException("Serialize array to string is not supported: " + val);
+        }
+
+        /** {@inheritDoc} */
+        @Override public Object processObjectValue(String key, Object val, JsonConfig jsonConfig) {
+            return val == null ? null : val.toString();
+        }
+    }
 }