You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by se...@apache.org on 2015/05/11 21:43:51 UTC

incubator-ignite git commit: # IGNITE-888 Web interface to monitoring cluster state. Initial commit.

Repository: incubator-ignite
Updated Branches:
  refs/heads/ignite-888 [created] c7a8427d5


# IGNITE-888 Web interface to monitoring cluster state.
Initial commit.


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

Branch: refs/heads/ignite-888
Commit: c7a8427d57bfb8e57347cfe1733d076035c3045b
Parents: 942abe4
Author: sevdokimov <se...@jetbrains.com>
Authored: Mon May 11 22:43:25 2015 +0300
Committer: sevdokimov <se...@jetbrains.com>
Committed: Mon May 11 22:43:25 2015 +0300

----------------------------------------------------------------------
 .../apache/ignite/internal/util/DebugUtil.java  |  76 +++++
 modules/webVisor/pom.xml                        | 292 +++++++++++++++++++
 .../ignite/webVisor/IgniteNameRepository.java   | 128 ++++++++
 .../ignite/webVisor/NodeSwitchFilter.java       | 140 +++++++++
 .../org/apache/ignite/webVisor/VisorServer.java | 181 ++++++++++++
 .../apache/ignite/webVisor/WebVisorPlugin.java  |  27 ++
 .../webVisor/WebVisorPluginConfiguration.java   |  59 ++++
 .../ignite/webVisor/WebVisorPluginProvider.java | 137 +++++++++
 .../apache/ignite/webVisor/WebVisorUtils.java   |  50 ++++
 .../webVisor/render/AbstractPageBean.java       |  60 ++++
 .../ignite/webVisor/render/ClusterPage.java     |  77 +++++
 .../ignite/webVisor/render/NodeInfoPage.java    |  34 +++
 .../org/apache/ignite/webVisor/render/WU.java   |  72 +++++
 .../org.apache.ignite.plugin.PluginProvider     |   1 +
 .../src/main/webapp/WEB-INF/sitemesh3.xml       |   3 +
 .../webVisor/src/main/webapp/WEB-INF/web.xml    |  28 ++
 modules/webVisor/src/main/webapp/caches.jsp     |  15 +
 modules/webVisor/src/main/webapp/cluster.jsp    |  52 ++++
 .../webVisor/src/main/webapp/css/cluster.css    |  10 +
 modules/webVisor/src/main/webapp/css/main.css   |   3 +
 modules/webVisor/src/main/webapp/decorator.jsp  |  59 ++++
 .../webVisor/src/main/webapp/igniteNotFound.jsp |  14 +
 .../src/main/webapp/moreThenOneIgnite.jsp       |  14 +
 .../webVisor/src/main/webapp/noIgnitesFound.jsp |  14 +
 modules/webVisor/src/main/webapp/nodeInfo.jsp   |  19 ++
 25 files changed, 1565 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/core/src/main/java/org/apache/ignite/internal/util/DebugUtil.java
----------------------------------------------------------------------
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/DebugUtil.java b/modules/core/src/main/java/org/apache/ignite/internal/util/DebugUtil.java
new file mode 100644
index 0000000..4876656
--- /dev/null
+++ b/modules/core/src/main/java/org/apache/ignite/internal/util/DebugUtil.java
@@ -0,0 +1,76 @@
+/*
+ * 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.internal.util;
+
+import java.util.*;
+
+/**
+ * 
+ */
+public class DebugUtil {
+    /** */
+    private static final DebugUtil INSTANCE = new DebugUtil();
+
+    /** */
+    private final Set<Thread> hangThreads = new HashSet<>();
+    
+    /**
+     *
+     */
+    private synchronized void hang0() {
+        hangThreads.add(Thread.currentThread());
+        
+        try {
+            while (hangThreads.contains(Thread.currentThread()))
+                wait();
+        }
+        catch (InterruptedException e) {
+            Thread.interrupted();
+        }
+    }
+    
+    /**
+     *
+     */
+    public synchronized Set<Thread> getHangThreads() {
+        return new HashSet<>(hangThreads);
+    }
+    
+    /**
+     *
+     */
+    public synchronized void resumeAll() {
+        hangThreads.clear();
+        
+        notifyAll();
+    }
+    
+    /**
+     *
+     */
+    public static void hang() {
+        INSTANCE.hang0();
+    }
+    
+    /**
+     *
+     */
+    public static DebugUtil getInstance() {
+        return INSTANCE;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/pom.xml
----------------------------------------------------------------------
diff --git a/modules/webVisor/pom.xml b/modules/webVisor/pom.xml
new file mode 100644
index 0000000..b8aeed7
--- /dev/null
+++ b/modules/webVisor/pom.xml
@@ -0,0 +1,292 @@
+<?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-visor</artifactId>
+    <version>1.1.0-SNAPSHOT</version>
+
+    <properties>
+        <jetty.version>9.2.10.v20150310</jetty.version>
+    </properties>
+
+    <dependencies>
+        <!--<dependency>-->
+            <!--<groupId>javax.servlet</groupId>-->
+            <!--<artifactId>javax.servlet-api</artifactId>-->
+            <!--<version>3.1.0</version>-->
+        <!--</dependency>-->
+
+        <!--<dependency>-->
+            <!--<groupId>javax.servlet.jsp</groupId>-->
+            <!--<artifactId>javax.servlet.jsp-api</artifactId>-->
+            <!--<version>2.3.2-b01</version>-->
+        <!--</dependency>-->
+
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-servlets</artifactId>
+            <version>${jetty.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.sitemesh</groupId>
+            <artifactId>sitemesh</artifactId>
+            <version>3.0.0</version>
+        </dependency>
+        
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-webapp</artifactId>
+            <version>${jetty.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-annotations</artifactId>
+            <version>${jetty.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-jsp</artifactId>
+            <version>${jetty.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>tomcat</groupId>
+            <artifactId>jasper-runtime</artifactId>
+            <version>5.5.23</version>
+
+            <exclusions>
+                <exclusion>
+                    <groupId>javax.servlet</groupId>
+                    <artifactId>servlet-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- Ignite -->
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-core</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-spring</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-indexing</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.11</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+            <resource>
+                <directory>src/main/webapp</directory>
+                <targetPath>webapp</targetPath>
+                <excludes>
+                    <exclude>*.jsp</exclude>
+                </excludes>
+            </resource>
+        </resources>
+
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.1</version>
+                <configuration>
+                    <source>1.7</source>
+                    <target>1.7</target>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>2.8</version>
+                <executions>
+                    <execution>
+                        <id>Copy dependencies</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>copy-dependencies</goal>
+                        </goals>
+
+                        <configuration>
+                            <outputDirectory>target/web-libs</outputDirectory>
+
+                            <excludeGroupIds>org.apache.ignite,org.glassfish.web,org.glassfish,org.eclipse.jetty.orbit</excludeGroupIds>
+                            <excludeArtifactIds>jetty-annotations,jetty-jsp,jetty-schemas</excludeArtifactIds>
+                        </configuration>
+                    </execution>
+                    <!--<execution>-->
+                        <!--<id>Copy</id>-->
+                        <!--<phase>package</phase>-->
+                        <!--<goals>-->
+                            <!--<goal>copy</goal>-->
+                        <!--</goals>-->
+
+                        <!--<configuration>-->
+                            <!--<outputDirectory>target/web-libs</outputDirectory>-->
+
+                            <!--<artifactItems>-->
+                                <!--<artifactItem>-->
+                                    <!--<groupId>javax.servlet</groupId>-->
+                                    <!--<artifactId>javax.servlet-api</artifactId>-->
+                                <!--</artifactItem>-->
+
+                                <!--<artifactItem>-->
+                                    <!--<groupId>javax.servlet.jsp</groupId>-->
+                                    <!--<artifactId>javax.servlet.jsp-api</artifactId>-->
+                                <!--</artifactItem>-->
+
+                                <!--<artifactItem>-->
+                                    <!--<groupId>org.eclipse.jetty</groupId>-->
+                                    <!--<artifactId>jetty-servlets</artifactId>-->
+                                    <!--<version>${jetty.version}</version>-->
+                                <!--</artifactItem>-->
+
+
+                                <!--<artifactItem>-->
+                                    <!--<groupId>org.eclipse.jetty</groupId>-->
+                                    <!--<artifactId>jetty-continuation</artifactId>-->
+                                    <!--<version>${jetty.version}</version>-->
+                                <!--</artifactItem>-->
+
+                                <!--<artifactItem>-->
+                                    <!--<groupId>org.eclipse.jetty</groupId>-->
+                                    <!--<artifactId>jetty-http</artifactId>-->
+                                    <!--<version>${jetty.version}</version>-->
+                                <!--</artifactItem>-->
+
+                                <!--<artifactItem>-->
+                                    <!--<groupId>org.eclipse.jetty</groupId>-->
+                                    <!--<artifactId>jetty-util</artifactId>-->
+                                    <!--<version>${jetty.version}</version>-->
+                                <!--</artifactItem>-->
+
+                                <!--<artifactItem>-->
+                                    <!--<groupId>org.eclipse.jetty</groupId>-->
+                                    <!--<artifactId>jetty-io</artifactId>-->
+                                    <!--<version>${jetty.version}</version>-->
+                                <!--</artifactItem>-->
+
+                                <!--<artifactItem>-->
+                                    <!--<groupId>org.eclipse.jetty</groupId>-->
+                                    <!--<artifactId>jetty-webapp</artifactId>-->
+                                    <!--<version>${jetty.version}</version>-->
+                                <!--</artifactItem>-->
+
+                                <!--<artifactItem>-->
+                                    <!--<groupId>org.eclipse.jetty</groupId>-->
+                                    <!--<artifactId>jetty-xml</artifactId>-->
+                                    <!--<version>${jetty.version}</version>-->
+                                <!--</artifactItem>-->
+
+                                <!--<artifactItem>-->
+                                    <!--<groupId>org.eclipse.jetty</groupId>-->
+                                    <!--<artifactId>jetty-servlet</artifactId>-->
+                                    <!--<version>${jetty.version}</version>-->
+                                <!--</artifactItem>-->
+
+                                <!--<artifactItem>-->
+                                    <!--<groupId>org.eclipse.jetty</groupId>-->
+                                    <!--<artifactId>jetty-security</artifactId>-->
+                                    <!--<version>${jetty.version}</version>-->
+                                <!--</artifactItem>-->
+
+                                <!--<artifactItem>-->
+                                    <!--<groupId>org.eclipse.jetty</groupId>-->
+                                    <!--<artifactId>jetty-server</artifactId>-->
+                                    <!--<version>${jetty.version}</version>-->
+                                <!--</artifactItem>-->
+
+                                <!--<artifactItem>-->
+                                    <!--<groupId>tomcat</groupId>-->
+                                    <!--<artifactId>jasper-runtime</artifactId>-->
+                                    <!--<version>5.5.23</version>-->
+                                <!--</artifactItem>-->
+                            <!--</artifactItems>-->
+                        <!--</configuration>-->
+                    <!--</execution>-->
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-jspc-maven-plugin</artifactId>
+                <version>${jetty.version}</version>
+                <executions>
+                    <execution>
+                        <id>jspc</id>
+                        <goals>
+                            <goal>jspc</goal>
+                        </goals>
+                        <configuration>
+                            <webXmlFragment>${basedir}/target/classes/webapp/WEB-INF/webfrag.xml</webXmlFragment>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/java/org/apache/ignite/webVisor/IgniteNameRepository.java
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/java/org/apache/ignite/webVisor/IgniteNameRepository.java b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/IgniteNameRepository.java
new file mode 100644
index 0000000..be41060
--- /dev/null
+++ b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/IgniteNameRepository.java
@@ -0,0 +1,128 @@
+/*
+ * 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.webVisor;
+
+import org.apache.ignite.*;
+import org.jetbrains.annotations.*;
+
+import java.util.*;
+import java.util.regex.*;
+
+/**
+ * Each unit test starts several nodes with name like "partitioned.GridCachePartitionedAtomicReferenceApiSelfTest0".
+ * Utl like http://localhost:8100/partitioned.GridCachePartitionedAtomicReferenceApiSelfTest0 lookslooks bad, We should 
+ * cut it to http://localhost:8100/g_0 , Ignite started by startGrids(1) will has suffix "g_1", Ignite started with 
+ * startGrids(2) will has suffix "g_2"
+ */
+public class IgniteNameRepository {
+    /** */
+    private static final IgniteNameRepository instance = new IgniteNameRepository();
+
+    /** 
+     * Names that should not be cut, For example TcpClientDiscoverySelfTest starts nodes with names like "server-1", 
+     * "client-2", in this case URL like http://localhost:8100/server-1 looks good. 
+     */
+    private static final Pattern GOOD_NAME_PATTERN = Pattern.compile("[0-9a-zA-Z\\-\\._]{1,15}");
+
+    /** Map short name to ignite name */
+    private final Map<String, String> shortName2Name = new HashMap<>();
+
+    /** Map short name to ignite name */
+    private final Map<String, String> name2shortName = new HashMap<>();
+
+    /**
+     * Default constructor.
+     */
+    private IgniteNameRepository() {
+        Ignition.addListener(new IgnitionListener() {
+            @Override public void onStateChange(@Nullable String name, IgniteState state) {
+                if (name != null && (state == IgniteState.STOPPED || state == IgniteState.STOPPED_ON_SEGMENTATION)) {
+                    synchronized (IgniteNameRepository.this) {
+                        String shortName = name2shortName.remove(name);
+
+                        shortName2Name.remove(shortName);
+                    }
+                }
+            }
+        });
+    }
+
+    /**
+     * @param shortName Short name.
+     */
+    @Nullable public synchronized String igniteName(@NotNull String shortName) {
+        return shortName2Name.get(shortName);
+    }
+
+    /**
+     * @param igniteName Ignite name.
+     */
+    public synchronized String getOrRegisterShortName(@NotNull String igniteName) {
+        assert !igniteName.isEmpty();
+
+        String res = name2shortName.get(igniteName);
+
+        if (res != null)
+            return res;
+
+        if (GOOD_NAME_PATTERN.matcher(igniteName).matches()) {
+            if (!shortName2Name.containsKey(igniteName)) {
+                shortName2Name.put(igniteName, igniteName);
+
+                name2shortName.put(igniteName, igniteName);
+
+                return igniteName;
+            }
+        }
+
+        char lastC = igniteName.charAt(igniteName.length() - 1);
+
+        if (lastC >= '0' && lastC <= '9') {
+            String oneNumName = igniteName.substring(igniteName.length() - 1);
+
+            if (!shortName2Name.containsKey(oneNumName)) {
+                shortName2Name.put(oneNumName, igniteName);
+
+                name2shortName.put(igniteName, oneNumName);
+
+                return oneNumName;
+            }
+        }
+
+        int idx = 100;
+
+        while (true) {
+            String shortName = "n" + idx;
+
+            if (!shortName2Name.containsKey(shortName)) {
+                shortName2Name.put(shortName, igniteName);
+
+                name2shortName.put(igniteName, shortName);
+
+                return shortName;
+            }
+        }
+    }
+
+    /**
+     * @return Instance
+     */
+    public static IgniteNameRepository instance() {
+        return instance;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/java/org/apache/ignite/webVisor/NodeSwitchFilter.java
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/java/org/apache/ignite/webVisor/NodeSwitchFilter.java b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/NodeSwitchFilter.java
new file mode 100644
index 0000000..c314a6a
--- /dev/null
+++ b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/NodeSwitchFilter.java
@@ -0,0 +1,140 @@
+/*
+ * 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.webVisor;
+
+import org.apache.ignite.*;
+import org.apache.ignite.webVisor.render.*;
+
+import javax.servlet.*;
+import javax.servlet.http.*;
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+/**
+ * 
+ */
+public class NodeSwitchFilter implements Filter {
+    /** */
+    private static final Pattern GRID_NAME_PATTERN = Pattern.compile("/g_([0-9a-zA-Z\\-\\._]+)(/.*)?");
+    
+    /** {@inheritDoc} */
+    @Override public void init(FilterConfig cfg) throws ServletException {
+        // No-op.
+    }
+
+    /**
+     * @param req Request.
+     * @param ignite Ignite.
+     */
+    private void initAttributes(HttpServletRequest req, Ignite ignite) {
+        req.setAttribute("g", ignite);
+        req.setAttribute("wu", new WU(ignite));
+    }
+    
+    /** {@inheritDoc} */
+    @Override public void doFilter(ServletRequest req, ServletResponse res,
+        FilterChain chain) throws IOException, ServletException {
+        HttpServletRequest httpReq = (HttpServletRequest)req;
+        HttpServletResponse httpResp = (HttpServletResponse)res;
+
+        Matcher matcher = GRID_NAME_PATTERN.matcher(httpReq.getRequestURI());
+        
+        if (!matcher.matches()) {
+            Ignite dfltIgnite;
+            
+            try {
+                dfltIgnite = Ignition.ignite();
+            }
+            catch (IgniteIllegalStateException e) {
+                dfltIgnite = null;
+            }
+
+            if (dfltIgnite != null) {
+                initAttributes(httpReq, dfltIgnite);
+                
+                chain.doFilter(req, res);
+                
+                return;
+            }
+            
+            List<Ignite> ignites = Ignition.allGrids();
+
+            if (ignites.isEmpty()) {
+                req.getRequestDispatcher("/noIgnitesFound.jsp").forward(req, res);
+                
+                return;
+            }
+
+            String igniteName = ignites.get(0).name();
+
+            if (igniteName == null || igniteName.length() == 0) {
+                initAttributes(httpReq, ignites.get(0));
+
+                chain.doFilter(req, res);
+                
+                return;
+            }
+
+            StringBuilder redirectUrl = new StringBuilder();
+            
+            redirectUrl.append(httpReq.getContextPath());
+            redirectUrl.append("/g_").append(IgniteNameRepository.instance().getOrRegisterShortName(igniteName));
+            redirectUrl.append(httpReq.getRequestURI());
+            
+            if (httpReq.getQueryString() != null)
+                redirectUrl.append('?').append(httpReq.getQueryString());
+            
+            httpResp.sendRedirect(redirectUrl.toString());
+            
+            return;
+        }
+
+        String shortName = matcher.group(1);
+
+        String igniteName = IgniteNameRepository.instance().igniteName(shortName);
+
+        Ignite ignite = null;
+        
+        if (igniteName != null) {
+            try {
+                ignite = Ignition.ignite(igniteName);
+            }
+            catch (IgniteIllegalStateException e) {
+                // No-op.
+            }
+        }
+        
+        if (ignite == null) {
+            req.getRequestDispatcher("/igniteNotFound.jsp").forward(req, res);
+            
+            return;
+        }
+        
+        initAttributes(httpReq, ignite);
+
+        String uri = matcher.group(2);
+        
+        httpReq.getRequestDispatcher(uri == null ? "/" : uri).forward(req, res);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void destroy() {
+        // No-op.
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/java/org/apache/ignite/webVisor/VisorServer.java
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/java/org/apache/ignite/webVisor/VisorServer.java b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/VisorServer.java
new file mode 100644
index 0000000..f404352
--- /dev/null
+++ b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/VisorServer.java
@@ -0,0 +1,181 @@
+/*
+ * 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.webVisor;
+
+import org.apache.ignite.*;
+import org.apache.ignite.internal.util.*;
+import org.apache.ignite.internal.util.typedef.internal.*;
+import org.eclipse.jetty.server.*;
+import org.eclipse.jetty.util.log.*;
+import org.eclipse.jetty.util.resource.*;
+import org.eclipse.jetty.webapp.*;
+import org.jetbrains.annotations.*;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+/**
+ *
+ */
+public class VisorServer {
+    /** */
+    private static VisorServer instance;
+    
+    /** */
+    private Server srv;
+
+    /** */
+    private int port = -1;
+
+    /** */
+    private final WebVisorPluginConfiguration cfg;
+    
+    /**
+     * @param cfg Config.
+     */
+    private VisorServer(WebVisorPluginConfiguration cfg) {
+        this.cfg = cfg;
+    }
+
+    /**
+     * @param cfg Config.
+     * @param log Logger.
+     */
+    public synchronized static VisorServer getOrStart(@NotNull WebVisorPluginConfiguration cfg, 
+        @Nullable IgniteLogger log) {
+        if (instance == null) {
+            instance = new VisorServer(cfg);
+            
+            instance.startup(log);
+        }
+        
+        return instance;
+    }
+    
+    /**
+     *
+     */
+    public boolean isStarted() {
+        return srv != null && srv.isStarted();
+    }
+    
+    /**
+     *
+     */
+    public void startup(@Nullable IgniteLogger log) {
+        try {
+            Properties p = new Properties();
+
+            p.setProperty("org.eclipse.jetty.LEVEL", "WARN");
+            p.setProperty("org.eclipse.jetty.util.log.LEVEL", "OFF");
+            p.setProperty("org.eclipse.jetty.util.component.LEVEL", "OFF");
+
+            StdErrLog.setProperties(p);
+
+            URL webXmlRes = VisorServer.class.getClassLoader().getResource("webapp/WEB-INF/web.xml");
+
+            String webXmlStr = webXmlRes.toString();
+
+            int port;
+
+            for (port = cfg.getFirstPort(); port < cfg.getFirstPort() + cfg.getPortRangeSize(); port++) {
+                Server srv = new Server(port);
+                
+                WebAppContext webAppCtx = new WebAppContext();
+
+                webAppCtx.setContextPath("/");
+
+                if (webXmlStr.startsWith("file:/")) {
+                    // Run from IDEA.
+                    File webApp = new File(webXmlRes.getFile()).getParentFile().getParentFile();
+                    File classes = webApp.getParentFile();
+                    File target = classes.getParentFile();
+                    File moduleRoot = target.getParentFile();
+
+                    webAppCtx.setResourceBase(moduleRoot.getAbsolutePath() + "/src/main/webapp");
+
+                    Configuration.ClassList classlist = Configuration.ClassList.setServerDefault(srv);
+                    classlist.addBefore(
+                        "org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
+                        "org.eclipse.jetty.annotations.AnnotationConfiguration" );
+
+                    // Set the ContainerIncludeJarPattern so that jetty examines these
+                    // container-path jars for tlds, web-fragments etc.
+                    // If you omit the jar that contains the jstl .tlds, the jsp engine will
+                    // scan for them instead.
+                    webAppCtx.setAttribute(
+                        "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
+                        ".*/[^/]*servlet-api-[^/]*\\.jar$|.*/javax.servlet.jsp.jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$" );
+                }
+                else {
+                    // Run from JAR
+                    URL webAppUrl = new URL(webXmlStr.substring(0, webXmlStr.length() - "WEB-INF/web.xml".length()));
+
+                    webAppCtx.setBaseResource(Resource.newResource(webAppUrl));
+                }
+
+                webAppCtx.setAttribute("debugUtil", DebugUtil.getInstance());
+
+                srv.setHandler(webAppCtx);
+
+                try {
+                    srv.start();
+
+                    this.srv = srv;
+
+                    break;
+                }
+                catch (BindException ignored) {
+                    // No-op.
+                }
+            }
+
+            if (srv == null) {
+                U.error(log, "Failed to start web server all ports are busy [firstPort=" + cfg.getFirstPort()
+                    + ", portsCount=" + cfg.getPortRangeSize() + "]");
+            }
+            else {
+                this.port = port;
+
+                if (log != null)
+                    log.info("Visor server started: http://localhost:" + port);
+            }
+        }
+        catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     *
+     */
+    public void stop() throws Exception {
+        if (srv != null)
+            srv.stop();
+    }
+
+    /**
+     *
+     */
+    public int getPort() {
+        assert isStarted();
+        
+        return port;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorPlugin.java
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorPlugin.java b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorPlugin.java
new file mode 100644
index 0000000..daad599
--- /dev/null
+++ b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorPlugin.java
@@ -0,0 +1,27 @@
+/*
+ * 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.webVisor;
+
+import org.apache.ignite.plugin.*;
+
+/**
+ *
+ */
+public class WebVisorPlugin implements IgnitePlugin {
+    // No-op.
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorPluginConfiguration.java
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorPluginConfiguration.java b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorPluginConfiguration.java
new file mode 100644
index 0000000..9bc339f
--- /dev/null
+++ b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorPluginConfiguration.java
@@ -0,0 +1,59 @@
+/*
+ * 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.webVisor;
+
+import org.apache.ignite.plugin.*;
+
+/**
+ *
+ */
+public class WebVisorPluginConfiguration implements PluginConfiguration {
+    /** */
+    private int firstPort = 8100;
+
+    /** */
+    private int portRangeSize = 10;
+
+    /**
+     *
+     */
+    public int getFirstPort() {
+        return firstPort;
+    }
+
+    /**
+     * @param firstPort First port.
+     */
+    public void setFirstPort(int firstPort) {
+        this.firstPort = firstPort;
+    }
+
+    /**
+     *
+     */
+    public int getPortRangeSize() {
+        return portRangeSize;
+    }
+
+    /**
+     * @param portRangeSize Port range size.
+     */
+    public void setPortRangeSize(int portRangeSize) {
+        this.portRangeSize = portRangeSize;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorPluginProvider.java
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorPluginProvider.java b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorPluginProvider.java
new file mode 100644
index 0000000..b51fdee
--- /dev/null
+++ b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorPluginProvider.java
@@ -0,0 +1,137 @@
+/*
+ * 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.webVisor;
+
+import org.apache.ignite.*;
+import org.apache.ignite.cluster.*;
+import org.apache.ignite.configuration.*;
+import org.apache.ignite.internal.*;
+import org.apache.ignite.plugin.*;
+import org.jetbrains.annotations.*;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ *
+ */
+public class WebVisorPluginProvider implements PluginProvider<WebVisorPluginConfiguration> {
+    /** */
+    private final WebVisorPlugin plugin = new WebVisorPlugin();
+
+    /** */
+    private WebVisorPluginConfiguration cfg;
+
+    /** */
+    private VisorServer srv;
+
+    /** */
+    private GridKernalContext igniteCtx;
+
+    /** {@inheritDoc} */
+    @Override public String name() {
+        return "web-visor";
+    }
+
+    /** {@inheritDoc} */
+    @Override public String version() {
+        return "1.0.0";
+    }
+
+    /** {@inheritDoc} */
+    @Override public String copyright() {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public WebVisorPlugin plugin() {
+        return plugin;
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public <T> T createComponent(PluginContext ctx, Class<T> cls) {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void initExtensions(PluginContext ctx, ExtensionRegistry registry) {
+        IgniteConfiguration igniteCfg = ctx.igniteConfiguration();
+
+        if (igniteCfg.getPluginConfigurations() != null) {
+            for (PluginConfiguration pluginCfg : igniteCfg.getPluginConfigurations()) {
+                if (pluginCfg instanceof WebVisorPluginConfiguration) {
+                    cfg = (WebVisorPluginConfiguration)pluginCfg;
+
+                    break;
+                }
+            }
+        }
+
+        if (cfg == null)
+            cfg = new WebVisorPluginConfiguration();
+
+        srv = VisorServer.getOrStart(cfg, ctx.log(VisorServer.class));
+
+        igniteCtx = ((IgniteKernal)ctx.grid()).context();
+    }
+
+    /** {@inheritDoc} */
+    @Override public void start(PluginContext ctx) throws IgniteCheckedException {
+        if (srv.isStarted()) {
+            igniteCtx.addNodeAttribute(WebVisorUtils.WEB_VISOR_NODE_PORT, srv.getPort());
+
+            String igniteName = ctx.grid().name();
+
+            if (igniteName != null && !igniteName.isEmpty()) {
+                String shortName = IgniteNameRepository.instance().getOrRegisterShortName(igniteName);
+                
+                igniteCtx.addNodeAttribute(WebVisorUtils.WEB_VISOR_NODE_URI, "/g_" + shortName);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public void stop(boolean cancel) throws IgniteCheckedException {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onIgniteStart() throws IgniteCheckedException {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onIgniteStop(boolean cancel) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Nullable @Override public Serializable provideDiscoveryData(UUID nodeId) {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void receiveDiscoveryData(UUID nodeId, Serializable data) {
+        // No-op.
+    }
+
+    /** {@inheritDoc} */
+    @Override public void validateNewNode(ClusterNode node) throws PluginValidationException {
+        // No-op.
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorUtils.java
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorUtils.java b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorUtils.java
new file mode 100644
index 0000000..91a3361
--- /dev/null
+++ b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/WebVisorUtils.java
@@ -0,0 +1,50 @@
+/*
+ * 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.webVisor;
+
+import org.apache.ignite.cluster.*;
+import org.apache.ignite.internal.util.typedef.*;
+
+/**
+ *
+ */
+public class WebVisorUtils {
+    /** */
+    public static final String WEB_VISOR_NODE_PORT = "web.visor.node.port";
+    
+    /** */
+    public static final String WEB_VISOR_NODE_URI = "web.visor.node.uri";
+    
+    
+    /**
+     * @param node Node.
+     */
+    public static String hostname(ClusterNode node) {
+        for (String hostName : node.hostNames()) {
+            if (!F.isEmpty(hostName))
+                return hostName;
+        }
+
+        for (String addr : node.addresses()) {
+            if (!F.isEmpty(addr))
+                return addr;
+        }
+        
+        return "unknown";
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/AbstractPageBean.java
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/AbstractPageBean.java b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/AbstractPageBean.java
new file mode 100644
index 0000000..6e3207c
--- /dev/null
+++ b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/AbstractPageBean.java
@@ -0,0 +1,60 @@
+/*
+ * 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.webVisor.render;
+
+import org.apache.ignite.*;
+import org.apache.ignite.cluster.*;
+import org.jetbrains.annotations.*;
+
+import javax.servlet.http.*;
+
+/**
+ *
+ */
+public class AbstractPageBean {
+    /** */
+    protected final HttpServletRequest req;
+
+    /** */
+    protected final Ignite ignite;
+
+    /**
+     * @param req Request.
+     */
+    public AbstractPageBean(HttpServletRequest req) {
+        this.req = req;
+
+        req.setAttribute("p", this);
+
+        ignite = (Ignite)req.getAttribute("g");
+    }
+
+    /**
+     *
+     */
+    @NotNull public Ignite getIgnite() {
+        return ignite;
+    }
+
+    /**
+     *
+     */
+    @NotNull public ClusterNode getLocalNode() {
+        return ignite.cluster().localNode();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/ClusterPage.java
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/ClusterPage.java b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/ClusterPage.java
new file mode 100644
index 0000000..d955dc2
--- /dev/null
+++ b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/ClusterPage.java
@@ -0,0 +1,77 @@
+/*
+ * 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.webVisor.render;
+
+import org.apache.ignite.cluster.*;
+import org.jetbrains.annotations.*;
+
+import javax.servlet.http.*;
+import java.util.*;
+
+/**
+ *
+ */
+public class ClusterPage extends AbstractPageBean {
+    /** */
+    private final List<ClusterNode> nodes;
+
+    /** */
+    private boolean showClients = true;
+
+    /**
+     * @param req Request.
+     */
+    public ClusterPage(@NotNull HttpServletRequest req) {
+        super(req);
+
+        nodes = new ArrayList<>();
+
+        if ("true".equals(req.getParameter("filter")))
+            showClients = req.getParameter("showClients") != null;
+
+        for (ClusterNode node : getIgnite().cluster().nodes()) {
+            if (showClients || !node.isClient())
+                nodes.add(node);
+        }
+
+        Collections.sort(nodes, new Comparator<ClusterNode>() {
+            @Override public int compare(ClusterNode o1, ClusterNode o2) {
+                int res = Long.compare(o1.order(), o2.order());
+
+                if (res != 0)
+                    return res;
+
+                return o1.id().compareTo(o2.id());
+            }
+        });
+    }
+
+    /**
+     *
+     */
+    @NotNull public List<ClusterNode> getNodes() {
+        return nodes;
+    }
+
+    /**
+     *
+     */
+    public boolean isShowClients() {
+        return showClients;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/NodeInfoPage.java
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/NodeInfoPage.java b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/NodeInfoPage.java
new file mode 100644
index 0000000..fce0f28
--- /dev/null
+++ b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/NodeInfoPage.java
@@ -0,0 +1,34 @@
+/*
+ * 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.webVisor.render;
+
+import org.jetbrains.annotations.*;
+
+import javax.servlet.http.*;
+
+/**
+ *
+ */
+public class NodeInfoPage extends AbstractPageBean {
+    /**
+     * @param req Request.
+     */
+    public NodeInfoPage(@NotNull HttpServletRequest req) {
+        super(req);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/WU.java
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/WU.java b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/WU.java
new file mode 100644
index 0000000..6d097c1
--- /dev/null
+++ b/modules/webVisor/src/main/java/org/apache/ignite/webVisor/render/WU.java
@@ -0,0 +1,72 @@
+/*
+ * 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.webVisor.render;
+
+import org.apache.ignite.*;
+import org.apache.ignite.cluster.*;
+import org.apache.ignite.spi.discovery.tcp.internal.*;
+import org.apache.ignite.webVisor.*;
+import org.jetbrains.annotations.*;
+
+/**
+ *
+ */
+public class WU {
+    /** */
+    private final Ignite g;
+
+    /**
+     * @param g G.
+     */
+    public WU(Ignite g) {
+        this.g = g;
+    }
+
+    /**
+     * @param node Node.
+     */
+    public String nodeAddr(ClusterNode node) {
+        String host = WebVisorUtils.hostname(node);
+        
+        if (!node.isClient() && node instanceof TcpDiscoveryNode)
+            return host + ':' + ((TcpDiscoveryNode)node).discoveryPort();
+        
+        return host;
+    }
+
+    /**
+     * @param node Node.
+     */
+    @Nullable public String visorUrl(ClusterNode node) {
+        Integer port = node.attribute(WebVisorUtils.WEB_VISOR_NODE_PORT);
+
+        if (port == null)
+            return null;
+
+        String uri = node.attribute(WebVisorUtils.WEB_VISOR_NODE_URI);
+
+        return "http://" + WebVisorUtils.hostname(node) + ':' + port + (uri == null ? "" : uri);
+    }
+    
+    /**
+     *
+     */
+    public ClusterNode localNode() {
+        return g.cluster().localNode();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/resources/META-INF/services/org.apache.ignite.plugin.PluginProvider
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/resources/META-INF/services/org.apache.ignite.plugin.PluginProvider b/modules/webVisor/src/main/resources/META-INF/services/org.apache.ignite.plugin.PluginProvider
new file mode 100644
index 0000000..4834987
--- /dev/null
+++ b/modules/webVisor/src/main/resources/META-INF/services/org.apache.ignite.plugin.PluginProvider
@@ -0,0 +1 @@
+org.apache.ignite.webVisor.WebVisorPluginProvider
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/webapp/WEB-INF/sitemesh3.xml
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/webapp/WEB-INF/sitemesh3.xml b/modules/webVisor/src/main/webapp/WEB-INF/sitemesh3.xml
new file mode 100644
index 0000000..6e432b1
--- /dev/null
+++ b/modules/webVisor/src/main/webapp/WEB-INF/sitemesh3.xml
@@ -0,0 +1,3 @@
+<sitemesh>
+    <mapping path="/*" decorator="/decorator.jsp"/>
+</sitemesh>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/webapp/WEB-INF/web.xml b/modules/webVisor/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..6fc8fb4
--- /dev/null
+++ b/modules/webVisor/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app version="2.4"
+         xmlns="http://java.sun.com/xml/ns/j2ee">
+    <welcome-file-list>
+        <welcome-file>nodeInfo.jsp</welcome-file>
+    </welcome-file-list>
+
+    <filter>
+        <filter-name>sitemesh</filter-name>
+        <filter-class>org.sitemesh.config.ConfigurableSiteMeshFilter</filter-class>
+    </filter>
+
+    <filter>
+        <filter-name>nodeSwitch</filter-name>
+        <filter-class>org.apache.ignite.webVisor.NodeSwitchFilter</filter-class>
+    </filter>
+
+    <filter-mapping>
+        <filter-name>sitemesh</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+
+    <filter-mapping>
+        <filter-name>nodeSwitch</filter-name>
+        <url-pattern>/*</url-pattern>
+    </filter-mapping>
+    
+</web-app>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/webapp/caches.jsp
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/webapp/caches.jsp b/modules/webVisor/src/main/webapp/caches.jsp
new file mode 100644
index 0000000..f493f7b
--- /dev/null
+++ b/modules/webVisor/src/main/webapp/caches.jsp
@@ -0,0 +1,15 @@
+<%--@elvariable id="g" type="org.apache.ignite.Ignite"--%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>Caches</title>
+</head>
+<body>
+
+<h2>Caches</h2>
+
+In development
+
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/webapp/cluster.jsp
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/webapp/cluster.jsp b/modules/webVisor/src/main/webapp/cluster.jsp
new file mode 100644
index 0000000..9aa895b
--- /dev/null
+++ b/modules/webVisor/src/main/webapp/cluster.jsp
@@ -0,0 +1,52 @@
+<%--@elvariable id="g" type="org.apache.ignite.Ignite"--%>
+<%--@elvariable id="wu" type="org.apache.ignite.webVisor.render.WU"--%>
+<%@ page import="org.apache.ignite.webVisor.render.*" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<!DOCTYPE html>
+<html lang="en">
+<% ClusterPage p = new ClusterPage(request); %>
+<%--@elvariable id="p" type="org.apache.ignite.webVisor.render.ClusterPage"--%>
+<head>
+    <title>${wu.nodeAddr(p.localNode)}: Cluster</title>
+
+    <link rel="stylesheet" href="css/cluster.css" type="text/css">
+</head>
+<body>
+
+<h2>Nodes in the cluster</h2>
+
+<div class="row">
+    <div class="col-md-6">
+        <c:forEach items="${p.nodes}" var="node">
+            <%--@elvariable id="node" type="org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode"--%>
+            <div class="cluster-node">
+                <span class="glyphicon ${node.client ? 'glyphicon-phone' : 'glyphicon-hdd'}"
+                      aria-hidden="true"
+                      title="${node.client ? 'Client node' : 'Server node'}"></span>
+
+                <strong>${wu.nodeAddr(node)}</strong> <br>
+                <small><span class="uuid">${node.id()}</span></small>
+            </div>
+        </c:forEach>
+    </div>
+
+    <div class="col-md-2">
+
+    </div>
+
+    <div class="col-md-4">
+        <h4>Filters</h4>
+
+        <form role="form" action="cluster.jsp">
+            <input type="hidden" name="filter" value="true">
+
+            <div class="checkbox">
+                <label><input type="checkbox" name="showClients" <%= p.isShowClients() ? "checked=checked" : "" %> onchange="submit()">Show clients</label>
+            </div>
+        </form>
+    </div>
+</div>
+
+
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/webapp/css/cluster.css
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/webapp/css/cluster.css b/modules/webVisor/src/main/webapp/css/cluster.css
new file mode 100644
index 0000000..1efecfe
--- /dev/null
+++ b/modules/webVisor/src/main/webapp/css/cluster.css
@@ -0,0 +1,10 @@
+.cluster-node {
+    padding: 3px;
+    margin-top: 20px;
+
+    border-radius: 4px;
+}
+
+.cluster-node .uuid {
+    color: gray;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/webapp/css/main.css
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/webapp/css/main.css b/modules/webVisor/src/main/webapp/css/main.css
new file mode 100644
index 0000000..2b2482c
--- /dev/null
+++ b/modules/webVisor/src/main/webapp/css/main.css
@@ -0,0 +1,3 @@
+.value {
+    font-weight: bold;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/webapp/decorator.jsp
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/webapp/decorator.jsp b/modules/webVisor/src/main/webapp/decorator.jsp
new file mode 100644
index 0000000..a5b1f8e
--- /dev/null
+++ b/modules/webVisor/src/main/webapp/decorator.jsp
@@ -0,0 +1,59 @@
+<%--@elvariable id="g" type="org.apache.ignite.Ignite"--%>
+<%--@elvariable id="wu" type="org.apache.ignite.webVisor.render.WU"--%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
+    <title><sitemesh:write property='title'/></title>
+
+    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
+
+    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
+    <!--[if lt IE 9]>
+    <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+    <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+    <![endif]-->
+
+    <link rel="stylesheet" href="css/main.css" type="text/css">
+    
+    <sitemesh:write property='head'/>
+</head>
+<body>
+<nav class="navbar navbar-default">
+    <div class="container">
+        <div class="navbar-header">
+            <a class="navbar-brand" href="${pageContext.request.contextPath}/">Ignite</a>
+        </div>
+        <div>
+            <ul class="nav navbar-nav">
+                <li><a href="nodeInfo.jsp">Node</a></li>
+                <li><a href="cluster.jsp">Cluster</a></li>
+                <li><a href="caches.jsp">Caches</a></li>
+            </ul>
+            <ul class="nav navbar-nav navbar-right">
+                <li style="color: gray">
+                    <small>Local node: ${wu.nodeAddr(wu.localNode())}</small>
+
+                    <c:if test="${g.name() != null}">
+                        <br> <small>Name: ${g.name()}</small>
+                    </c:if>
+                </li>
+            </ul>
+        </div>
+    </div>
+</nav>
+
+<div class="container">
+    <sitemesh:write property='body'/>
+</div>
+
+<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
+<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
+<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/webapp/igniteNotFound.jsp
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/webapp/igniteNotFound.jsp b/modules/webVisor/src/main/webapp/igniteNotFound.jsp
new file mode 100644
index 0000000..8e6b6f6
--- /dev/null
+++ b/modules/webVisor/src/main/webapp/igniteNotFound.jsp
@@ -0,0 +1,14 @@
+<%--@elvariable id="g" type="org.apache.ignite.Ignite"--%>
+<%--@elvariable id="wu" type="org.apache.ignite.webVisor.render.WU"--%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>Ignite not found</title>
+</head>
+<body>
+
+Ignite not found
+
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/webapp/moreThenOneIgnite.jsp
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/webapp/moreThenOneIgnite.jsp b/modules/webVisor/src/main/webapp/moreThenOneIgnite.jsp
new file mode 100644
index 0000000..0c3693f
--- /dev/null
+++ b/modules/webVisor/src/main/webapp/moreThenOneIgnite.jsp
@@ -0,0 +1,14 @@
+<%--@elvariable id="g" type="org.apache.ignite.Ignite"--%>
+<%--@elvariable id="wu" type="org.apache.ignite.webVisor.render.WU"--%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>Error</title>
+</head>
+<body>
+
+More then one Ignite found with id8=${igniteId8}
+
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/webapp/noIgnitesFound.jsp
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/webapp/noIgnitesFound.jsp b/modules/webVisor/src/main/webapp/noIgnitesFound.jsp
new file mode 100644
index 0000000..176d253
--- /dev/null
+++ b/modules/webVisor/src/main/webapp/noIgnitesFound.jsp
@@ -0,0 +1,14 @@
+<%--@elvariable id="g" type="org.apache.ignite.Ignite"--%>
+<%--@elvariable id="wu" type="org.apache.ignite.webVisor.render.WU"--%>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>Ignite no ignites run in JVM</title>
+</head>
+<body>
+
+Ignite no ignites run in JVM
+
+</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/c7a8427d/modules/webVisor/src/main/webapp/nodeInfo.jsp
----------------------------------------------------------------------
diff --git a/modules/webVisor/src/main/webapp/nodeInfo.jsp b/modules/webVisor/src/main/webapp/nodeInfo.jsp
new file mode 100644
index 0000000..89e1d7f
--- /dev/null
+++ b/modules/webVisor/src/main/webapp/nodeInfo.jsp
@@ -0,0 +1,19 @@
+<%--@elvariable id="g" type="org.apache.ignite.Ignite"--%>
+<%--@elvariable id="wu" type="org.apache.ignite.webVisor.render.WU"--%>
+<%@ page import="org.apache.ignite.webVisor.render.*" %>
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<!DOCTYPE html>
+<html lang="en">
+<% NodeInfoPage p = new NodeInfoPage(request); %>
+<%--@elvariable id="p" type="org.apache.ignite.webVisor.render.NodeInfoPage"--%>
+<head>
+    <title>${wu.nodeAddr(p.localNode)}: Cluster</title>
+
+    <%--<link rel="stylesheet" href="css/cluster.css" type="text/css">--%>
+</head>
+<body>
+
+In development
+
+</body>
+</html>
\ No newline at end of file