You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ak...@apache.org on 2015/07/02 10:27:03 UTC

[8/9] incubator-ignite git commit: # ignite-843 Cleanup module.

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/form-models/clusters.json
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/form-models/clusters.json b/modules/web-control-center/nodejs/public/form-models/clusters.json
new file mode 100644
index 0000000..3258cc8
--- /dev/null
+++ b/modules/web-control-center/nodejs/public/form-models/clusters.json
@@ -0,0 +1,849 @@
+{
+  "templateTip": [
+    "Use following template for add cluster:",
+    "<ul>",
+    "  <li>blank - Empty configuration.</li>",
+    "  <li>local - Configuration with static ips discovery and pre-configured list of IP addresses.</li>",
+    "  <li>multicast - Configuration with multicast discovery.</li>",
+    "</ul>"
+  ],
+  "general": [
+    {
+      "label": "Name",
+      "type": "text",
+      "model": "name",
+      "required": true,
+      "placeholder": "Input name"
+    },
+    {
+      "label": "Caches",
+      "type": "dropdown-multiple",
+      "model": "caches",
+      "placeholder": "Choose caches",
+      "items": "caches",
+      "tip": [
+        "Select caches to start in cluster."
+      ],
+      "addLink": {
+        "label": "Add cache(s)",
+        "ref": "/caches"
+      }
+    },
+    {
+      "label": "Discovery",
+      "type": "dropdown-details",
+      "path": "discovery",
+      "model": "kind",
+      "required": true,
+      "placeholder": "Choose discovery",
+      "items": "discoveries",
+      "tip": [
+        "Discovery allows to discover remote nodes in grid."
+      ],
+      "details": {
+        "Vm": {
+          "expanded": true,
+          "fields": [
+            {
+              "tableLabel": "Addresses",
+              "label": "address",
+              "type": "table-simple",
+              "path": "discovery.Vm",
+              "model": "addresses",
+              "editIdx": -1,
+              "reordering": true,
+              "placeholder": "IP address:port",
+              "tip": [
+                "Addresses may be represented as follows:",
+                "<ul>",
+                "  <li>IP address (e.g. 127.0.0.1, 9.9.9.9, etc);</li>",
+                "  <li>IP address and port (e.g. 127.0.0.1:47500, 9.9.9.9:47501, etc);</li>",
+                "  <li>IP address and port range (e.g. 127.0.0.1:47500..47510, 9.9.9.9:47501..47504, etc);</li>",
+                "  <li>Hostname (e.g. host1.com, host2, etc);</li>",
+                "  <li>Hostname and port (e.g. host1.com:47500, host2:47502, etc).</li>",
+                "  <li>Hostname and port range (e.g. host1.com:47500..47510, host2:47502..47508, etc).</li>",
+                "</ul>",
+                "If port is 0 or not provided then default port will be used (depends on discovery SPI configuration).",
+                "If port range is provided (e.g. host:port1..port2) the following should be considered:",
+                "<ul>",
+                "  <li>port1 < port2 should be true;</li>",
+                "  <li>Both port1 and port2 should be greater than 0.</li>",
+                "</ul>"
+              ]
+            }
+          ]
+        },
+        "Multicast": {
+          "expanded": false,
+          "fields": [
+            {
+              "label": "IP address",
+              "type": "text",
+              "path": "discovery.Multicast",
+              "model": "multicastGroup",
+              "placeholder": "228.1.2.4",
+              "tip": [
+                "IP address of multicast group."
+              ]
+            },
+            {
+              "label": "Port number",
+              "type": "number",
+              "path": "discovery.Multicast",
+              "model": "multicastPort",
+              "max": 65535,
+              "placeholder": "47400",
+              "tip": [
+                "Port number which multicast messages are sent to."
+              ]
+            },
+            {
+              "label": "Waits for reply",
+              "type": "number",
+              "path": "discovery.Multicast",
+              "model": "responseWaitTime",
+              "placeholder": "500",
+              "tip": [
+                "Time in milliseconds IP finder waits for reply to multicast address request."
+              ]
+            },
+            {
+              "label": "Attempts count",
+              "type": "number",
+              "path": "discovery.Multicast",
+              "model": "addressRequestAttempts",
+              "placeholder": "2",
+              "tip": [
+                "Number of attempts to send multicast address request.",
+                "IP finder re-sends request only in case if no reply for previous request is received."
+              ]
+            },
+            {
+              "label": "Local address",
+              "type": "text",
+              "path": "discovery.Multicast",
+              "model": "localAddress",
+              "tip": [
+                "Local host address used by this IP finder.",
+                "If provided address is non-loopback then multicast socket is bound to this interface.",
+                "If local address is not set or is any local address then IP finder creates multicast sockets for all found non-loopback addresses."
+              ]
+            }
+          ]
+        },
+        "S3": {
+          "expanded": true,
+          "fields": [
+            {
+              "label": "Bucket name",
+              "type": "text",
+              "path": "discovery.S3",
+              "model": "bucketName",
+              "placeholder": ""
+            }
+          ]
+        },
+        "Cloud": {
+          "expanded": true,
+          "fields": [
+            {
+              "label": "Credential",
+              "type": "text",
+              "path": "discovery.Cloud",
+              "model": "credential",
+              "placeholder": "",
+              "tip": [
+                "Credential that is used during authentication on the cloud.",
+                "Depending on a cloud platform it can be a password or access key."
+              ]
+            },
+            {
+              "label": "Path to credential",
+              "type": "text",
+              "path": "discovery.Cloud",
+              "model": "credentialPath",
+              "placeholder": "",
+              "tip": [
+                "Path to a credential that is used during authentication on the cloud.",
+                "Access key or private key should be stored in a plain or PEM file without a passphrase."
+              ]
+            },
+            {
+              "label": "Identity",
+              "type": "text",
+              "path": "discovery.Cloud",
+              "model": "identity",
+              "placeholder": "",
+              "tip": [
+                "Identity that is used as a user name during a connection to the cloud.",
+                "Depending on a cloud platform it can be an email address, user name, etc."
+              ]
+            },
+            {
+              "label": "Provider",
+              "type": "text",
+              "model": "discovery.Cloud.provider",
+              "placeholder": "",
+              "tip": [
+                "Cloud provider to use."
+              ]
+            }
+          ]
+        },
+        "GoogleStorage": {
+          "expanded": true,
+          "fields": [
+            {
+              "label": "Project name",
+              "type": "text",
+              "path": "discovery.GoogleStorage",
+              "model": "projectName",
+              "placeholder": "",
+              "tip": [
+                "Google Cloud Platforms project name.",
+                "Usually this is an auto generated project number (ex. 208709979073) that can be found in 'Overview' section of Google Developer Console."
+              ]
+            },
+            {
+              "label": "Bucket name",
+              "type": "text",
+              "path": "discovery.GoogleStorage",
+              "model": "bucketName",
+              "placeholder": "",
+              "tip": [
+                "Google Cloud Storage bucket name.",
+                "If the bucket doesn't exist Ignite will automatically create it.",
+                "However the name must be unique across whole Google Cloud Storage and Service Account Id must be authorized to perform this operation."
+              ]
+            },
+            {
+              "label": "Private key path",
+              "type": "text",
+              "path": "discovery.GoogleStorage",
+              "model": "serviceAccountP12FilePath",
+              "placeholder": "",
+              "tip": [
+                "Full path to the private key in PKCS12 format of the Service Account."
+              ]
+            },
+            {
+              "label": "Account id",
+              "type": "text",
+              "path": "discovery.GoogleStorage",
+              "model": "accountId",
+              "placeholder": "",
+              "tip": [
+                "Service account ID (typically an e-mail address)."
+              ]
+            }
+          ]
+        },
+        "Jdbc": {
+          "expanded": true,
+          "fields": [
+            {
+              "label": "DB schema should be initialized by Ignite",
+              "type": "check",
+              "path": "discovery.Jdbc",
+              "model": "initSchema",
+              "tip": [
+                "Flag indicating whether DB schema should be initialized by Ignite or was explicitly created by user."
+              ]
+            }
+          ]
+        },
+        "SharedFs": {
+          "expanded": false,
+          "fields": [
+            {
+              "label": "File path",
+              "type": "text",
+              "path": "discovery.SharedFs",
+              "model": "path",
+              "placeholder": "disco/tcp"
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "advanced": [
+    {
+      "label": "Atomic configuration",
+      "tip": [
+        "Configuration for atomic data structures.",
+        "Atomics are distributed across the cluster, essentially enabling performing atomic operations (such as increment-and-get or compare-and-set) with the same globally-visible value."
+      ],
+      "fields": [
+        {
+          "label": "Backups",
+          "type": "number",
+          "path": "atomicConfiguration",
+          "model": "backups",
+          "placeholder": "0",
+          "tip": [
+            "Number of backup nodes."
+          ]
+        },
+        {
+          "label": "Cache mode",
+          "type": "dropdown",
+          "path": "atomicConfiguration",
+          "model": "cacheMode",
+          "placeholder": "PARTITIONED",
+          "items": "cacheModes",
+          "tip": [
+            "Cache modes:",
+            "<ul>",
+            "  <li>Partitioned - in this mode the overall key set will be divided into partitions and all partitions will be split equally between participating nodes.</li>",
+            "  <li>Replicated - in this mode all the keys are distributed to all participating nodes.</li>",
+            "  <li>Local - in this mode caches residing on different grid nodes will not know about each other.</li>",
+            "</ul>"
+          ]
+        },
+        {
+          "label": "Sequence reserve",
+          "type": "number",
+          "path": "atomicConfiguration",
+          "model": "atomicSequenceReserveSize",
+          "placeholder": "1,000",
+          "tip": [
+            "Default number of sequence values reserved for IgniteAtomicSequence instances.",
+            "After a certain number has been reserved, consequent increments of sequence will happen locally, without communication with other nodes, until the next reservation has to be made."
+          ]
+        }
+      ]
+    },
+    {
+      "label": "Communication",
+      "tip": [
+        "Cluster communication network properties."
+      ],
+      "fields": [
+        {
+          "label": "Timeout",
+          "type": "number",
+          "model": "networkTimeout",
+          "placeholder": "5,000",
+          "tip": [
+            "Maximum timeout in milliseconds for network requests."
+          ]
+        },
+        {
+          "label": "Send retry delay",
+          "type": "number",
+          "model": "networkSendRetryDelay",
+          "placeholder": "1,000",
+          "tip": [
+            "Interval in milliseconds between message send retries."
+          ]
+        },
+        {
+          "label": "Send retry count",
+          "type": "number",
+          "model": "networkSendRetryCount",
+          "placeholder": "3",
+          "tip": [
+            "Message send retries count."
+          ]
+        },
+        {
+          "label": "Segment check frequency",
+          "type": "number",
+          "model": "segmentCheckFrequency",
+          "placeholder": "10,000",
+          "tip": [
+            "Network segment check frequency in milliseconds.",
+            "If 0, periodic segment check is disabled and segment is checked only on topology changes (if segmentation resolvers are configured)."
+          ]
+        },
+        {
+          "label": "Wait for segment on start",
+          "type": "check",
+          "model": "waitForSegmentOnStart",
+          "tip": [
+            "Wait for segment on start flag.",
+            "<ul>",
+            "  <li>If enabled, node should wait for correct segment on start.</li>",
+            "  <li>If node detects that segment is incorrect on startup and enabled, node waits until segment becomes correct.</li>",
+            "  <li>If segment is incorrect on startup and disabled, exception is thrown.</li>",
+            "</ul>"
+          ]
+        },
+        {
+          "label": "Discovery startup delay",
+          "type": "number",
+          "model": "discoveryStartupDelay",
+          "placeholder": "600,000",
+          "tip": [
+            "This value is used to expire messages from waiting list whenever node discovery discrepancies happen."
+          ]
+        }
+      ]
+    },
+    {
+      "label": "Deployment",
+      "tip": [
+        "Task and resources deployment in cluster."
+      ],
+      "fields": [
+        {
+          "label": "Mode",
+          "type": "dropdown",
+          "model": "deploymentMode",
+          "placeholder": "SHARED",
+          "items": "deploymentModes",
+          "tip": [
+            "Task classes and resources sharing mode."
+          ]
+        }
+      ]
+    },
+    {
+      "label": "Events",
+      "tip": [
+        " Grid events are used for notification about what happens within the grid."
+      ],
+      "fields": [
+        {
+          "label": "Include type",
+          "type": "dropdown-multiple",
+          "model": "includeEventTypes",
+          "placeholder": "Choose recorded event types",
+          "items": "events",
+          "tip": [
+            "Array of event types, which will be recorded by GridEventStorageManager#record(Event).",
+            "Note, that either the include event types or the exclude event types can be established."
+          ]
+        }
+      ]
+    },
+    {
+      "label": "Marshaller",
+      "tip": [
+        "Marshaller allows to marshal or unmarshal objects in grid.",
+        "It provides serialization/deserialization mechanism for all instances that are sent across networks or are otherwise serialized."
+      ],
+      "fields": [
+        {
+          "label": "Marshaller",
+          "type": "dropdown-details",
+          "path": "marshaller",
+          "model": "kind",
+          "placeholder": "Choose marshaller",
+          "items": "marshallers",
+          "tip": [
+            "Instance of marshaller to use in grid. If not provided, OptimizedMarshaller will be used on Java HotSpot VM, and JdkMarshaller will be used on other VMs."
+          ],
+          "details": {
+            "OptimizedMarshaller": {
+              "expanded": false,
+              "fields": [
+                {
+                  "label": "Streams pool size",
+                  "type": "number",
+                  "path": "marshaller.OptimizedMarshaller",
+                  "model": "poolSize",
+                  "placeholder": "0",
+                  "tip": [
+                    "Specifies size of cached object streams used by marshaller.",
+                    "Object streams are cached for performance reason to avoid costly recreation for every serialization routine.",
+                    "If 0 (default), pool is not used and each thread has its own cached object stream which it keeps reusing.",
+                    "Since each stream has an internal buffer, creating a stream for each thread can lead to high memory consumption if many large messages are marshalled or unmarshalled concurrently.",
+                    "Consider using pool in this case. This will limit number of streams that can be created and, therefore, decrease memory consumption.",
+                    "NOTE: Using streams pool can decrease performance since streams will be shared between different threads which will lead to more frequent context switching."
+                  ]
+                },
+                {
+                  "label": "Require serializable",
+                  "type": "check",
+                  "path": "marshaller.OptimizedMarshaller",
+                  "model": "requireSerializable",
+                  "tip": [
+                    "Whether marshaller should require Serializable interface or not."
+                  ]
+                }
+              ]
+            }
+          }
+        },
+        {
+          "label": "Marshal local jobs",
+          "type": "check",
+          "model": "marshalLocalJobs",
+          "placeholder": "false",
+          "tip": [
+            "If this flag is enabled, jobs mapped to local node will be marshalled as if it was remote node."
+          ]
+        },
+        {
+          "label": "Keep alive time",
+          "type": "number",
+          "model": "marshallerCacheKeepAliveTime",
+          "placeholder": "10,000",
+          "tip": [
+            "Keep alive time of thread pool that is in charge of processing marshaller messages."
+          ]
+        },
+        {
+          "label": "Pool size",
+          "type": "number",
+          "model": "marshallerCacheThreadPoolSize",
+          "placeholder": "max(8, availableProcessors) * 2",
+          "tip": [
+            "Default size of thread pool that is in charge of processing marshaller messages."
+          ]
+        }
+      ]
+    },
+    {
+      "label": "Metrics",
+      "tip": ["Cluster runtime metrics settings."],
+      "fields": [
+        {
+          "label": "Elapsed time",
+          "type": "number",
+          "model": "metricsExpireTime",
+          "placeholder": "Long.MAX_VALUE",
+          "min": 1,
+          "tip": [
+            "Time in milliseconds after which a certain metric value is considered expired."
+          ]
+        },
+        {
+          "label": "History size",
+          "type": "number",
+          "model": "metricsHistorySize",
+          "placeholder": "10,000",
+          "min": 1,
+          "tip": [
+            "Number of metrics kept in history to compute totals and averages."
+          ]
+        },
+        {
+          "label": "Log frequency",
+          "type": "number",
+          "model": "metricsLogFrequency",
+          "placeholder": "60,000",
+          "tip": [
+            "Frequency of metrics log print out. To disable set to 0"
+          ]
+        },
+        {
+          "label": "Update frequency",
+          "type": "number",
+          "model": "metricsUpdateFrequency",
+          "placeholder": "60,000",
+          "tip": [
+            "Job metrics update frequency in milliseconds.",
+            "<ul>",
+            "  <li>If set to -1 job metrics are never updated.</li>",
+            "  <li>If set to 0 job metrics are updated on each job start and finish.</li>",
+            "  <li>Positive value defines the actual update frequency.</li>",
+            "</ul>"
+          ]
+        }
+      ]
+    },
+    {
+      "label": "Peer Class Loading",
+      "tip": ["Cluster peer class loading settings."],
+      "fields": [
+        {
+          "label": "Enable peer class loading",
+          "type": "check",
+          "model": "peerClassLoadingEnabled",
+          "tip": [
+            "Enables/disables peer class loading."
+          ]
+        },
+        {
+          "label": "Local class path exclude",
+          "type": "text",
+          "model": "peerClassLoadingLocalClassPathExclude",
+          "placeholder": "[]",
+          "tip": [
+            "List of packages separated by comma from the system classpath that need to be peer-to-peer loaded from task originating node.",
+            "'*' is supported at the end of the package name which means that all sub-packages and their classes are included like in Java package import clause."
+          ]
+        },
+        {
+          "label": "Missed resources cache size",
+          "type": "number",
+          "model": "peerClassLoadingMissedResourcesCacheSize",
+          "placeholder": "100",
+          "tip": [
+            "If size greater than 0, missed resources will be cached and next resource request ignored.",
+            "If size is 0, then request for the resource will be sent to the remote node every time this resource is requested."
+          ]
+        },
+        {
+          "label": "Pool size",
+          "type": "number",
+          "model": "peerClassLoadingThreadPoolSize",
+          "placeholder": "availableProcessors",
+          "tip": [
+            "Thread pool size to use for peer class loading."
+          ]
+        }
+      ]
+    },
+    {
+      "label": "Swap",
+      "tip": ["Settings for overflow data to disk if it cannot fit in memory."],
+      "fields": [
+        {
+          "label": "Swap space SPI",
+          "type": "dropdown-details",
+          "path": "swapSpaceSpi",
+          "model": "kind",
+          "items": "swapSpaceSpis",
+          "placeholder": "Choose swap SPI",
+          "tip": [
+            "Provides a mechanism in grid for storing data on disk.",
+            "Ignite cache uses swap space to overflow data to disk if it cannot fit in memory."
+          ],
+          "details": {
+            "FileSwapSpaceSpi": {
+              "fields": [
+                {
+                  "label": "Base directory",
+                  "type": "text",
+                  "path": "swapSpaceSpi.FileSwapSpaceSpi",
+                  "model": "baseDirectory",
+                  "placeholder": "swapspace",
+                  "tip": [
+                    "Base directory where to write files."
+                  ]
+                },
+                {
+                  "label": "Read stripe size",
+                  "type": "number",
+                  "path": "swapSpaceSpi.FileSwapSpaceSpi",
+                  "model": "readStripesNumber",
+                  "placeholder": "available CPU cores",
+                  "tip": [
+                    "Read stripe size defines number of file channels to be used concurrently."
+                  ]
+                },
+                {
+                  "label": "Maximum sparsity",
+                  "type": "number",
+                  "path": "swapSpaceSpi.FileSwapSpaceSpi",
+                  "model": "maximumSparsity",
+                  "placeholder": "0.5",
+                  "tip": [
+                    "This property defines maximum acceptable wasted file space to whole file size ratio.",
+                    "When this ratio becomes higher than specified number compacting thread starts working."
+                  ]
+                },
+                {
+                  "label": "Max write queue size",
+                  "type": "number",
+                  "path": "swapSpaceSpi.FileSwapSpaceSpi",
+                  "model": "maxWriteQueueSize",
+                  "placeholder": "1024 * 1024",
+                  "tip": [
+                    "Max write queue size in bytes.",
+                    "If there are more values are waiting for being written to disk then specified size, SPI will block on store operation."
+                  ]
+                },
+                {
+                  "label": "Write buffer size",
+                  "type": "number",
+                  "path": "swapSpaceSpi.FileSwapSpaceSpi",
+                  "model": "writeBufferSize",
+                  "placeholder": "Available CPU cores",
+                  "tip": [
+                    "Write buffer size in bytes.",
+                    "Write to disk occurs only when this buffer is full."
+                  ]
+                }
+              ]
+            }
+          }
+        }
+      ]
+    },
+    {
+      "label": "Time configuration",
+      "tip": ["Time settings for CLOCK write ordering mode."],
+      "fields": [
+        {
+          "label": "Samples size",
+          "type": "number",
+          "model": "clockSyncSamples",
+          "placeholder": "8",
+          "tip": [
+            "Number of samples used to synchronize clocks between different nodes.",
+            "Clock synchronization is used for cache version assignment in CLOCK order mode."
+          ]
+        },
+        {
+          "label": "Frequency",
+          "type": "number",
+          "model": "clockSyncFrequency",
+          "placeholder": "120,000",
+          "tip": [
+            "Frequency at which clock is synchronized between nodes, in milliseconds.",
+            "Clock synchronization is used for cache version assignment in CLOCK order mode."
+          ]
+        },
+        {
+          "label": "Port base",
+          "type": "number",
+          "model": "timeServerPortBase",
+          "max": 65535,
+          "placeholder": "31100",
+          "tip": [
+            "Time server provides clock synchronization between nodes.",
+            "Base UPD port number for grid time server. Time server will be started on one of free ports in range."
+          ]
+        },
+        {
+          "label": "Port range",
+          "type": "number",
+          "model": "timeServerPortRange",
+          "placeholder": "100",
+          "tip": [
+            "Time server port range."
+          ]
+        }
+      ]
+    },
+    {
+      "label": "Thread pools size",
+      "tip": ["Settings for node thread pools."],
+      "fields": [
+        {
+          "label": "Public",
+          "type": "number",
+          "model": "publicThreadPoolSize",
+          "placeholder": "max(8, availableProcessors) * 2",
+          "tip": [
+            "Thread pool that is in charge of processing ComputeJob, GridJobs and user messages sent to node."
+          ]
+        },
+        {
+          "label": "System",
+          "type": "number",
+          "model": "systemThreadPoolSize",
+          "placeholder": "max(8, availableProcessors) * 2",
+          "tip": [
+            "Thread pool that is in charge of processing internal system messages."
+          ]
+        },
+        {
+          "label": "Management",
+          "type": "number",
+          "model": "managementThreadPoolSize",
+          "placeholder": "4",
+          "tip": [
+            "Thread pool that is in charge of processing internal and Visor ComputeJob, GridJobs."
+          ]
+        },
+        {
+          "label": "IGFS",
+          "type": "number",
+          "model": "igfsThreadPoolSize",
+          "placeholder": "availableProcessors",
+          "tip": [
+            "Thread pool that is in charge of processing outgoing IGFS messages."
+          ]
+        }
+      ]
+    },
+    {
+      "label": "Transactions",
+      "tip": ["Settings for transactions."],
+      "fields": [
+        {
+          "label": "Cache concurrency",
+          "type": "dropdown",
+          "path": "transactionConfiguration",
+          "model": "defaultTxConcurrency",
+          "placeholder": "PESSIMISTIC",
+          "items": "transactionConcurrency",
+          "tip": [
+            "Cache transaction concurrency to use when one is not explicitly specified."
+          ]
+        },
+        {
+          "label": "Isolation",
+          "type": "dropdown",
+          "path": "transactionConfiguration",
+          "model": "transactionIsolation",
+          "placeholder": "REPEATABLE_READ",
+          "items": "transactionIsolation",
+          "tip": [
+            "Default transaction isolation."
+          ]
+        },
+        {
+          "label": "Default timeout",
+          "type": "number",
+          "path": "transactionConfiguration",
+          "model": "defaultTxTimeout",
+          "placeholder": "0",
+          "tip": [
+            "Default transaction timeout."
+          ]
+        },
+        {
+          "label": "Pessimistic log cleanup delay",
+          "type": "number",
+          "path": "transactionConfiguration",
+          "model": "pessimisticTxLogLinger",
+          "placeholder": "10,000",
+          "tip": [
+            "Delay, in milliseconds, after which pessimistic recovery entries will be cleaned up for failed node."
+          ]
+        },
+        {
+          "label": "Pessimistic log size",
+          "type": "number",
+          "path": "transactionConfiguration",
+          "model": "pessimisticTxLogSize",
+          "placeholder": "0",
+          "tip": [
+            "Size of pessimistic transactions log stored on node in order to recover transaction commit if originating node has left grid before it has sent all messages to transaction nodes."
+          ]
+        },
+        {
+          "label": "Enable serializable cache transactions",
+          "type": "check",
+          "path": "transactionConfiguration",
+          "model": "txSerializableEnabled",
+          "tip": [
+            "Flag to enable/disable isolation level for cache transactions.",
+            "Serializable level does carry certain overhead and if not used, should be disabled."
+          ]
+        }
+      ]
+    },
+    {
+      "label": "Utility",
+      "tip": ["Settings for utility messages processing."],
+      "fields": [
+        {
+          "label": "Keep alive time",
+          "type": "number",
+          "model": "utilityCacheKeepAliveTime",
+          "placeholder": "10,000",
+          "tip": [
+            "Keep alive time of thread pool that is in charge of processing utility cache messages."
+          ]
+        },
+        {
+          "label": "Pool size",
+          "type": "number",
+          "model": "utilityCachePoolSize",
+          "placeholder": "max(8, availableProcessors) * 2",
+          "tip": [
+            "Thread pool that is in charge of processing utility cache messages."
+          ]
+        }
+      ]
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/form-models/persistence.json
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/form-models/persistence.json b/modules/web-control-center/nodejs/public/form-models/persistence.json
new file mode 100644
index 0000000..edf5344
--- /dev/null
+++ b/modules/web-control-center/nodejs/public/form-models/persistence.json
@@ -0,0 +1,66 @@
+{
+  "connection": [
+    {
+      "label": "Name",
+      "type": "text",
+      "model": "name",
+      "required": true
+    },
+    {
+      "label": "Database type",
+      "type": "dropdown",
+      "model": "dbType",
+      "placeholder": "Choose database",
+      "items": "databases",
+      "tip": [
+        "Select database type to connect for loading tables metadata."
+      ]
+    },
+    {
+      "label": "Database name",
+      "type": "text",
+      "model": "dbName",
+      "tip": [
+        "Database name to connect for loading tables metadata."
+      ]
+    },
+    {
+      "label": "Host",
+      "type": "text",
+      "model": "host",
+      "placeholder": "IP address or host",
+      "tip": [
+        "IP address or host name where database server deployed."
+      ]
+    },
+    {
+      "label": "Port",
+      "type": "number",
+      "model": "port",
+      "max": 65535,
+      "placeholder": "",
+      "tip": [
+        "Port number for connecting to database."
+      ]
+    },
+    {
+      "label": "User",
+      "type": "text",
+      "model": "user",
+      "placeholder": "",
+      "tip": [
+        "User name for connecting to database."
+      ]
+    },
+    {
+      "label": "Password",
+      "type": "password",
+      "model": "password",
+      "placeholder": "",
+      "tip": [
+        "Password for connecting to database.",
+        "Note, password would not be saved."
+      ]
+    }
+  ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/images/docker.png
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/images/docker.png b/modules/web-control-center/nodejs/public/images/docker.png
new file mode 100644
index 0000000..7ec3aef
Binary files /dev/null and b/modules/web-control-center/nodejs/public/images/docker.png differ

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/images/java.png
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/images/java.png b/modules/web-control-center/nodejs/public/images/java.png
new file mode 100644
index 0000000..ddb3b8e
Binary files /dev/null and b/modules/web-control-center/nodejs/public/images/java.png differ

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/images/logo.png
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/images/logo.png b/modules/web-control-center/nodejs/public/images/logo.png
new file mode 100644
index 0000000..c3577c5
Binary files /dev/null and b/modules/web-control-center/nodejs/public/images/logo.png differ

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/images/xml.png
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/images/xml.png b/modules/web-control-center/nodejs/public/images/xml.png
new file mode 100644
index 0000000..029065e
Binary files /dev/null and b/modules/web-control-center/nodejs/public/images/xml.png differ

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/javascripts/bundle.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/javascripts/bundle.js b/modules/web-control-center/nodejs/public/javascripts/bundle.js
new file mode 100644
index 0000000..840da56
--- /dev/null
+++ b/modules/web-control-center/nodejs/public/javascripts/bundle.js
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+// Place here all common utility functions that will be available on each page.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/javascripts/controllers/caches.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/javascripts/controllers/caches.js b/modules/web-control-center/nodejs/public/javascripts/controllers/caches.js
new file mode 100644
index 0000000..006f065
--- /dev/null
+++ b/modules/web-control-center/nodejs/public/javascripts/controllers/caches.js
@@ -0,0 +1,237 @@
+/*
+ * 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.
+ */
+
+configuratorModule.controller('cachesController', ['$scope', '$alert', '$http', 'commonFunctions', function ($scope, $alert, $http, commonFunctions) {
+        $scope.swapSimpleItems = commonFunctions.swapSimpleItems;
+        $scope.joinTip = commonFunctions.joinTip;
+        $scope.getModel = commonFunctions.getModel;
+
+        $scope.atomicities = [
+            {value: 'ATOMIC', label: 'ATOMIC'},
+            {value: 'TRANSACTIONAL', label: 'TRANSACTIONAL'}
+        ];
+
+        $scope.modes = [
+            {value: 'PARTITIONED', label: 'PARTITIONED'},
+            {value: 'REPLICATED', label: 'REPLICATED'},
+            {value: 'LOCAL', label: 'LOCAL'}
+        ];
+
+        $scope.atomicWriteOrderModes = [
+            {value: 'CLOCK', label: 'CLOCK'},
+            {value: 'PRIMARY', label: 'PRIMARY'}
+        ];
+
+        $scope.memoryModes = [
+            {value: 'ONHEAP_TIERED', label: 'ONHEAP_TIERED'},
+            {value: 'OFFHEAP_TIERED', label: 'OFFHEAP_TIERED'},
+            {value: 'OFFHEAP_VALUES', label: 'OFFHEAP_VALUES'}
+        ];
+
+        $scope.evictionPolicies = [
+            {value: 'LRU', label: 'Least Recently Used'},
+            {value: 'RND', label: 'Random'},
+            {value: 'FIFO', label: 'FIFO'},
+            {value: 'SORTED', label: 'Sorted'},
+            {value: undefined, label: 'Not set'}
+        ];
+
+        $scope.rebalanceModes = [
+            {value: 'SYNC', label: 'SYNC'},
+            {value: 'ASYNC', label: 'ASYNC'},
+            {value: 'NONE', label: 'NONE'}
+        ];
+
+        $scope.cacheStoreFactories = [
+            {value: 'CacheJdbcPojoStoreFactory', label: 'JDBC POJO store factory'},
+            {value: 'CacheJdbcBlobStoreFactory', label: 'JDBC BLOB store factory'},
+            {value: 'CacheHibernateBlobStoreFactory', label: 'Hibernate BLOB store factory'},
+            {value: undefined, label: 'Not set'}
+        ];
+
+        $scope.cacheStoreJdbcDialects = [
+            {value: 'BasicJdbcDialect', label: 'Generic JDBC dialect'},
+            {value: 'OracleDialect', label: 'Oracle'},
+            {value: 'DB2Dialect', label: 'IBM DB2'},
+            {value: 'SQLServerDialect', label: 'Microsoft SQL Server'},
+            {value: 'MySQLDialect', label: 'My SQL'},
+            {value: 'H2Dialect', label: 'H2 database'}
+        ];
+
+        $scope.general = [];
+        $scope.advanced = [];
+
+        $http.get('/form-models/caches.json')
+            .success(function (data) {
+                $scope.general = data.general;
+                $scope.advanced = data.advanced;
+            });
+
+        $scope.caches = [];
+
+        // When landing on the page, get caches and show them.
+        $http.get('/rest/caches')
+            .success(function (data) {
+                $scope.spaces = data.spaces;
+                $scope.caches = data.caches;
+
+                var restoredItem = angular.fromJson(sessionStorage.cacheBackupItem);
+
+                if (restoredItem) {
+                    var idx = _.findIndex($scope.caches, function (cache) {
+                        return cache._id == restoredItem._id;
+                    });
+
+                    if (idx >= 0)
+                        $scope.selectedItem = $scope.caches[idx];
+
+                    $scope.backupItem = restoredItem;
+                }
+
+                $scope.$watch('backupItem', function (val) {
+                    if (val)
+                        sessionStorage.cacheBackupItem = angular.toJson(val);
+                }, true);
+            });
+
+        $scope.selectItem = function (item) {
+            $scope.selectedItem = item;
+
+            $scope.backupItem = angular.copy(item);
+        };
+
+        // Add new cache.
+        $scope.createItem = function () {
+            $scope.backupItem = {mode: 'PARTITIONED', atomicityMode: 'ATOMIC'};
+            $scope.backupItem.space = $scope.spaces[0]._id;
+        };
+
+        // Save cache in db.
+        $scope.saveItem = function () {
+            var item = $scope.backupItem;
+
+            console.log(item);
+
+            if (item.cacheStoreFactory && !item.readThrough && !item.writeThrough) {
+                $alert({position: 'top', title: 'Store is configured but read/write through are not enabled!'});
+
+                return;
+            }
+
+            if ((item.readThrough || item.writeThrough) && (!item.cacheStoreFactory || !item.cacheStoreFactory.kind)) {
+                $alert({position: 'top', title: 'Read / write through are enabled but strore is not configured!'});
+
+                return;
+            }
+
+            $http.post('/rest/caches/save', item)
+                .success(function (_id) {
+                    var idx = _.findIndex($scope.caches, function (cache) {
+                        return cache._id == _id;
+                    });
+
+                    if (idx >= 0)
+                        angular.extend($scope.caches[idx], item);
+                    else {
+                        item._id = _id;
+
+                        $scope.caches.push(item);
+                    }
+
+                    $scope.selectItem(item);
+
+                    $alert({
+                        type: 'success',
+                        title: 'Cache "' + item.name + '" saved.',
+                        duration: 2,
+                        container: '#save-btn'
+                    });
+                })
+                .error(function (errorMessage) {
+                    $alert({title: errorMessage});
+                });
+        };
+
+        $scope.removeItem = function () {
+            var _id = $scope.selectedItem._id;
+
+            $http.post('/rest/caches/remove', {_id: _id})
+                .success(function () {
+                    var i = _.findIndex($scope.caches, function (cache) {
+                        return cache._id == _id;
+                    });
+
+                    if (i >= 0) {
+                        $scope.caches.splice(i, 1);
+
+                        $scope.selectedItem = undefined;
+                        $scope.backupItem = undefined;
+                    }
+                })
+                .error(function (errorMessage) {
+                    $alert({title: errorMessage});
+                });
+        };
+
+        $scope.addIndexedTypes = function (keyCls, valCls) {
+            var idxTypes = $scope.backupItem.indexedTypes;
+
+            var newItem = {keyClass: keyCls, valueClass: valCls};
+
+            if (idxTypes)
+                idxTypes.push(newItem);
+            else
+                $scope.backupItem.indexedTypes = [newItem];
+        };
+
+        $scope.editIndexedTypes = function (idx) {
+            $scope.indexedTypeIdx = idx;
+
+            if (idx < 0) {
+                $scope.currKeyCls = '';
+                $scope.currValCls = '';
+            }
+            else {
+                var idxType = $scope.backupItem.indexedTypes[idx];
+
+                $scope.currKeyCls = idxType.keyClass;
+                $scope.currValCls = idxType.valueClass;
+            }
+        };
+
+        $scope.saveIndexedType = function (k, v) {
+            var idxTypes = $scope.backupItem.indexedTypes;
+
+            var idx = $scope.indexedTypeIdx;
+
+            if (idx < 0) {
+                var newItem = {keyClass: k, valueClass: v};
+
+                if (idxTypes)
+                    idxTypes.push(newItem);
+                else
+                    $scope.backupItem.indexedTypes = [newItem];
+            }
+            else {
+                var idxType = idxTypes[idx];
+
+                idxType.keyClass = k;
+                idxType.valueClass = v;
+            }
+        };
+    }]
+);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/javascripts/controllers/clusters.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/javascripts/controllers/clusters.js b/modules/web-control-center/nodejs/public/javascripts/controllers/clusters.js
new file mode 100644
index 0000000..ce38381
--- /dev/null
+++ b/modules/web-control-center/nodejs/public/javascripts/controllers/clusters.js
@@ -0,0 +1,207 @@
+/*
+ * 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.
+ */
+
+configuratorModule.controller('clustersController', ['$scope', '$alert', '$http', 'commonFunctions', function ($scope, $alert, $http, commonFunctions) {
+        $scope.swapSimpleItems = commonFunctions.swapSimpleItems;
+        $scope.joinTip = commonFunctions.joinTip;
+        $scope.getModel = commonFunctions.getModel;
+
+        $scope.templates = [
+            {value: {}, label: 'blank'},
+            {value: {discovery: {kind: 'Vm', Vm: {addresses: ['127.0.0.1:47500..47510']}}}, label: 'local'},
+            {value: {discovery: {kind: 'Multicast', Multicast: {}}}, label: 'multicast'}
+        ];
+
+        $scope.discoveries = [
+            {value: 'Vm', label: 'static IPs'},
+            {value: 'Multicast', label: 'multicast'},
+            {value: 'S3', label: 'AWS S3'},
+            {value: 'Cloud', label: 'apache jclouds'},
+            {value: 'GoogleStorage', label: 'google cloud storage'},
+            {value: 'Jdbc', label: 'JDBC'},
+            {value: 'SharedFs', label: 'shared filesystem'}
+        ];
+
+        $scope.swapSpaceSpis = [
+            {value: 'FileSwapSpaceSpi', label: 'File-based swap'},
+            {value: undefined, label: 'Not set'}
+        ];
+
+        $scope.events = [];
+
+        for (var eventGroupName in eventGroups) {
+            if (eventGroups.hasOwnProperty(eventGroupName)) {
+                $scope.events.push({value: eventGroupName, label: eventGroupName});
+            }
+        }
+
+        $scope.cacheModes = [
+            {value: 'LOCAL', label: 'LOCAL'},
+            {value: 'REPLICATED', label: 'REPLICATED'},
+            {value: 'PARTITIONED', label: 'PARTITIONED'}
+        ];
+
+        $scope.deploymentModes = [
+            {value: 'PRIVATE', label: 'PRIVATE'},
+            {value: 'ISOLATED', label: 'ISOLATED'},
+            {value: 'SHARED', label: 'SHARED'},
+            {value: 'CONTINUOUS', label: 'CONTINUOUS'}
+        ];
+
+        $scope.transactionConcurrency = [
+            {value: 'OPTIMISTIC', label: 'OPTIMISTIC'},
+            {value: 'PESSIMISTIC', label: 'PESSIMISTIC'}
+        ];
+
+        $scope.transactionIsolation = [
+            {value: 'READ_COMMITTED', label: 'READ_COMMITTED'},
+            {value: 'REPEATABLE_READ', label: 'REPEATABLE_READ'},
+            {value: 'SERIALIZABLE', label: 'SERIALIZABLE'}
+        ];
+
+        $scope.segmentationPolicy = [
+            {value: 'RESTART_JVM', label: 'RESTART_JVM'},
+            {value: 'STOP', label: 'STOP'},
+            {value: 'NOOP', label: 'NOOP'}
+        ];
+
+        $scope.marshallers = [
+            {value: 'JdkMarshaller', label: 'JdkMarshaller'},
+            {value: 'OptimizedMarshaller', label: 'OptimizedMarshaller'}
+        ];
+
+        $scope.clusters = [];
+
+        $http.get('/form-models/clusters.json')
+            .success(function (data) {
+                $scope.templateTip = data.templateTip;
+
+                $scope.general = data.general;
+                $scope.advanced = data.advanced;
+            });
+
+        // When landing on the page, get clusters and show them.
+        $http.get('/rest/clusters')
+            .success(function (data) {
+                $scope.caches = data.caches;
+                $scope.spaces = data.spaces;
+                $scope.clusters = data.clusters;
+
+                var restoredItem = angular.fromJson(sessionStorage.clusterBackupItem);
+
+                if (restoredItem) {
+                    var idx = _.findIndex($scope.clusters, function (cluster) {
+                        return cluster._id == restoredItem._id;
+                    });
+
+                    if (idx >= 0)
+                        $scope.selectedItem = $scope.clusters[idx];
+
+                    $scope.backupItem = restoredItem;
+                }
+
+                $scope.$watch('backupItem', function (val) {
+                    if (val)
+                        sessionStorage.clusterBackupItem = angular.toJson(val);
+                }, true);
+            });
+
+        $scope.selectItem = function (item) {
+            $scope.selectedItem = item;
+
+            $scope.backupItem = angular.copy(item);
+        };
+
+        // Add new cluster.
+        $scope.createItem = function () {
+            $scope.backupItem = angular.copy($scope.create.template);
+
+            $scope.backupItem.space = $scope.spaces[0]._id;
+        };
+
+        // Save cluster in db.
+        $scope.saveItem = function () {
+            var item = $scope.backupItem;
+
+            if (!item.swapSpaceSpi || !item.swapSpaceSpi.kind) {
+                for (var cacheId in item.caches) {
+                    var idx = _.findIndex($scope.caches, function (cache) {
+                        return cache._id == cacheId.value;
+                    });
+
+                    if (idx >= 0) {
+                        var cache = $scope.caches[idx];
+
+                        if (cache.swapEnabled) {
+                            $alert({title: 'Swap space SPI is not configured, but cache "' + cache.label + '" configured to use swap!'});
+
+                            return;
+                        }
+                    }
+                }
+            }
+
+            $http.post('/rest/clusters/save', item)
+                .success(function (_id) {
+                    var idx = _.findIndex($scope.clusters, function (cluster) {
+                        return cluster._id == _id;
+                    });
+
+                    if (idx >= 0)
+                        angular.extend($scope.clusters[idx], item);
+                    else {
+                        item._id = _id;
+
+                        $scope.clusters.push(item);
+                    }
+
+                    $scope.selectItem(item);
+
+                    $alert({
+                        type: 'success',
+                        title: 'Cluster "' + item.name + '" saved.',
+                        duration: 2,
+                        container: '#save-btn'
+                    });
+                })
+                .error(function (errorMessage) {
+                    $alert({title: errorMessage});
+                });
+        };
+
+        $scope.removeItem = function () {
+            var _id = $scope.selectedItem._id;
+
+            $http.post('/rest/clusters/remove', {_id: _id})
+                .success(function () {
+                    var i = _.findIndex($scope.clusters, function (cluster) {
+                        return cluster._id == _id;
+                    });
+
+                    if (i >= 0) {
+                        $scope.clusters.splice(i, 1);
+
+                        $scope.selectedItem = undefined;
+                        $scope.backupItem = undefined;
+                    }
+                })
+                .error(function (errorMessage) {
+                    $alert({title: errorMessage});
+                });
+        };
+    }]
+);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/javascripts/controllers/common.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/javascripts/controllers/common.js b/modules/web-control-center/nodejs/public/javascripts/controllers/common.js
new file mode 100644
index 0000000..4d1e411
--- /dev/null
+++ b/modules/web-control-center/nodejs/public/javascripts/controllers/common.js
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var configuratorModule = angular.module('ignite-web-configurator', ['smart-table', 'mgcrea.ngStrap', 'ngSanitize']);
+
+configuratorModule.service('commonFunctions', function () {
+    return {
+        getModel: function(obj, path) {
+            if (!path)
+                return obj;
+
+            path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
+            path = path.replace(/^\./, '');           // strip a leading dot
+
+            var segs = path.split('.');
+            var root = obj;
+
+            while (segs.length > 0) {
+                var pathStep = segs.shift();
+
+                if (typeof root[pathStep] === 'undefined')
+                    root[pathStep] = {};
+
+                root = root[pathStep];
+            }
+
+            return root;
+        },
+        swapSimpleItems: function (a, ix1, ix2) {
+            var tmp = a[ix1];
+
+            a[ix1] = a[ix2];
+            a[ix2] = tmp;
+        },
+        joinTip: function(arr) {
+            if (!arr) {
+                return arr;
+            }
+
+            var lines = arr.map(function (line) {
+                var rtrimmed = line.replace(/\s+$/g, '');
+
+                if (rtrimmed.indexOf('>', this.length - 1) == -1) {
+                    rtrimmed = rtrimmed + '<br/>';
+                }
+
+                return rtrimmed;
+            });
+
+            return lines.join("");
+        }
+    }
+});
+
+configuratorModule.config(function ($tooltipProvider) {
+    angular.extend($tooltipProvider.defaults, {
+        container: 'body',
+        placement: 'right',
+        html: 'true',
+        trigger: 'click hover'
+        //,delay: { hide: 600 }
+    });
+});
+
+configuratorModule.config(function ($selectProvider) {
+    angular.extend($selectProvider.defaults, {
+        maxLength: '1',
+        allText: 'Select All',
+        noneText: 'Clear All',
+        template: '/select'
+    });
+});
+
+// Alert settings
+configuratorModule.config(function ($alertProvider) {
+    angular.extend($alertProvider.defaults, {
+        container: 'body',
+        placement: 'top-right',
+        duration: '5',
+        type: 'danger'
+    });
+});
+
+// Decode name using map(value, label).
+configuratorModule.filter('displayValue', function () {
+    return function (v, m, dflt) {
+        var i = _.findIndex(m, function (item) {
+            return item.value == v;
+        });
+
+        if (i >= 0) {
+            return m[i].label;
+        }
+
+        if (dflt) {
+            return dflt;
+        }
+
+        return 'Unknown value';
+    }
+});
+
+/**
+ * Replaces all occurrences of {@code org.apache.ignite.} with {@code o.a.i.},
+ * {@code org.apache.ignite.internal.} with {@code o.a.i.i.},
+ * {@code org.apache.ignite.internal.visor.} with {@code o.a.i.i.v.} and
+ * {@code org.apache.ignite.scalar.} with {@code o.a.i.s.}.
+ *
+ * @param s String to replace in.
+ * @return Replaces string.
+ */
+configuratorModule.filter('compact', function () {
+    return function (s) {
+        return s.replace("org.apache.ignite.internal.visor.", "o.a.i.i.v.").
+            replace("org.apache.ignite.internal.", "o.a.i.i.").
+            replace("org.apache.ignite.scalar.", "o.a.i.s.").
+            replace("org.apache.ignite.", "o.a.i.");
+    }
+});
+
+configuratorModule.controller('activeLink', ['$scope', function ($scope) {
+    $scope.isActive = function (path) {
+        return window.location.pathname.substr(0, path.length) == path;
+    };
+}]);
+
+configuratorModule.controller('auth', ['$scope', '$modal', '$alert', '$http', '$window', function ($scope, $modal, $alert, $http, $window) {
+    $scope.action = 'login';
+
+    $scope.errorMessage = '';
+
+    $scope.valid = false;
+
+    // Pre-fetch an external template populated with a custom scope
+    var authModal = $modal({scope: $scope, template: '/login', show: false});
+
+    $scope.login = function () {
+        // Show when some event occurs (use $promise property to ensure the template has been loaded)
+        authModal.$promise.then(authModal.show);
+    };
+
+    $scope.auth = function (action, user_info) {
+        $http.post('/rest/auth/' + action, user_info)
+            .success(function (data) {
+                authModal.hide();
+
+                $window.location = '/clusters';
+            })
+            .error(function (data) {
+                console.log(data);
+
+                $alert({placement: 'top', container: '#errors-container', title: data});
+            });
+    };
+}]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/javascripts/controllers/persistences.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/javascripts/controllers/persistences.js b/modules/web-control-center/nodejs/public/javascripts/controllers/persistences.js
new file mode 100644
index 0000000..10e3f8f
--- /dev/null
+++ b/modules/web-control-center/nodejs/public/javascripts/controllers/persistences.js
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+configuratorModule.controller('persistenceController', ['$scope', '$alert', '$http', 'commonFunctions', function ($scope, $alert, $http, commonFunctions) {
+        $scope.joinTip = commonFunctions.joinTip;
+        $scope.getModel = commonFunctions.getModel;
+
+        $scope.databases = [
+            {value: 'oracle', label: 'Oracle database'},
+            {value: 'db2', label: 'IBM DB2'},
+            {value: 'mssql', label: 'MS SQL Server'},
+            {value: 'postgre', label: 'PostgreSQL'},
+            {value: 'mysql', label: 'MySQL'},
+            {value: 'h2', label: 'H2 database'}
+        ];
+
+        $scope.connection = [];
+
+        $http.get('/form-models/persistence.json')
+            .success(function (data) {
+                $scope.connection = data.connection;
+            });
+
+        $scope.persistences = [];
+
+        // When landing on the page, get persistences and show them.
+        $http.get('/rest/persistences')
+            .success(function (data) {
+                $scope.spaces = data.spaces;
+                $scope.persistences = data.persistences;
+
+                var restoredItem = angular.fromJson(sessionStorage.persistenceBackupItem);
+
+                if (restoredItem) {
+                    var idx = _.findIndex($scope.persistences, function (persistence) {
+                        return persistence._id == restoredItem._id;
+                    });
+
+                    if (idx >= 0)
+                        $scope.selectedItem = $scope.persistences[idx];
+
+                    $scope.backupItem = restoredItem;
+                }
+
+                $scope.$watch('backupItem', function (val) {
+                    if (val)
+                        sessionStorage.persistenceBackupItem = angular.toJson(val);
+                }, true);
+            });
+
+        $scope.selectItem = function (item) {
+            $scope.selectedItem = item;
+            $scope.backupItem = angular.copy(item);
+        };
+
+        // Add new persistence.
+        $scope.createItem = function () {
+            $scope.backupItem = {database: 'oracle'};
+            $scope.backupItem.space = $scope.spaces[0]._id;
+        };
+
+        // Save persistence in db.
+        $scope.saveItem = function () {
+            var item = $scope.backupItem;
+
+            $http.post('/rest/persistences/save', item)
+                .success(function (_id) {
+                    var i = _.findIndex($scope.persistences, function (persistence) {
+                        return persistence._id == _id;
+                    });
+
+                    if (i >= 0)
+                        angular.extend($scope.persistences[i], item);
+                    else {
+                        item._id = _id;
+
+                        $scope.persistences.push(item);
+                    }
+
+                    $scope.selectItem(item);
+                })
+                .error(function (errorMessage) {
+                    $alert({title: errorMessage});
+                });
+        };
+
+        $scope.removeItem = function () {
+            var _id = $scope.selectedItem._id;
+
+            $http.post('/rest/persistences/remove', {_id: _id})
+                .success(function () {
+                    var i = _.findIndex($scope.persistences, function (persistence) {
+                        return persistence._id == _id;
+                    });
+
+                    if (i >= 0) {
+                        $scope.persistences.splice(i, 1);
+
+                        $scope.selectedItem = undefined;
+                        $scope.backupItem = undefined;
+                    }
+                })
+                .error(function (errorMessage) {
+                    $alert({title: errorMessage});
+                });
+        };
+
+        $scope.data = {
+            curTableIdx: -1,
+            curFieldIdx: -1,
+            curKeyClass: '',
+            curValueClass: '',
+            curJavaName: '',
+            curJavaType: '',
+            tables: [
+                {schemaName: 'Schema1', use: true},
+                {schemaName: 'Schema1', use: true, tableName: 'Table1', keyClass: 'KeyClass1', valueClass: 'ValueClass1',
+                    fields: [
+                        {use: true, key: true, ak: true, dbName: 'name1', dbType: 'dbType1', javaName: 'javaName1', javaType: 'javaType1'},
+                        {use: true, key: false, ak: false, dbName: 'name2', dbType: 'dbType2', javaName: 'javaName2', javaType: 'javaType2'},
+                        {use: false, key: false, ak: false, dbName: 'name3', dbType: 'dbType3', javaName: 'javaName3', javaType: 'javaType3'}
+                    ]
+                },
+                {schemaName: 'Schema2 with very long name', use: false},
+                {schemaName: 'Schema2', use: false, tableName: 'Table2', keyClass: 'KeyClass2', valueClass: 'ValueClass2',
+                    fields: [
+                        {use: true, key: true, ak: true, dbName: 'name4', dbType: 'dbType4', javaName: 'javaName4', javaType: 'javaType4'},
+                        {use: true, key: false, ak: false, dbName: 'name5', dbType: 'dbType5', javaName: 'javaName5', javaType: 'javaType5'},
+                        {use: false, key: false, ak: false, dbName: 'name6', dbType: 'dbType6', javaName: 'javaName6', javaType: 'javaType6'}
+                    ]},
+                {schemaName: 'Schema2', use: false, tableName: 'Table3', keyClass: 'KeyClass3', valueClass: 'ValueClass3',
+                    fields: [
+                        {use: true, key: true, ak: true, dbName: 'name7', dbType: 'dbType7', javaName: 'javaName7', javaType: 'javaType7'},
+                        {use: true, key: false, ak: false, dbName: 'name8', dbType: 'dbType8', javaName: 'javaName8', javaType: 'javaType8'},
+                        {use: false, key: false, ak: false, dbName: 'name9', dbType: 'dbType9', javaName: 'javaName9', javaType: 'javaType9'},
+                        {use: false, key: false, ak: false, dbName: 'name10', dbType: 'dbType10', javaName: 'javaName10', javaType: 'javaType10'},
+                        {use: false, key: false, ak: false, dbName: 'name11', dbType: 'dbType11', javaName: 'javaName11', javaType: 'javaType11'},
+                        {use: false, key: false, ak: false, dbName: 'name12', dbType: 'dbType12', javaName: 'javaName12', javaType: 'javaType12'}
+                    ]}]
+        };
+
+        $scope.selectSchema = function (idx) {
+            var data = $scope.data;
+            var tables = data.tables;
+            var schemaName = tables[idx].schemaName;
+            var use = tables[idx].use;
+
+            for (var i = idx + 1; i < tables.length; i++) {
+                var item = tables[i];
+
+                if (item.schemaName == schemaName && item.tableName)
+                    item.use = use;
+                else
+                    break;
+            }
+
+            data.curTableIdx = -1;
+            data.curFieldIdx = -1;
+        };
+
+        $scope.selectTable = function (idx) {
+            var data = $scope.data;
+
+            data.curTableIdx = idx;
+            data.curFieldIdx = -1;
+
+            if (idx >= 0) {
+                var tbl = data.tables[idx];
+
+                data.curKeyClass = tbl.keyClass;
+                data.curValueClass = tbl.valueClass;
+            }
+        };
+
+        $scope.selectField = function (idx) {
+            var data = $scope.data;
+
+            data.curFieldIdx = idx;
+
+            if (idx >= 0) {
+                var fld = data.tables[data.curTableIdx].fields[idx];
+
+                data.curJavaName = fld.javaName;
+                data.curJavaType = fld.javaType;
+            }
+        };
+    }]
+);

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/javascripts/controllers/summary.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/javascripts/controllers/summary.js b/modules/web-control-center/nodejs/public/javascripts/controllers/summary.js
new file mode 100644
index 0000000..53cac48
--- /dev/null
+++ b/modules/web-control-center/nodejs/public/javascripts/controllers/summary.js
@@ -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.
+ */
+
+configuratorModule.controller('summaryController', ['$scope', '$http', function ($scope, $http) {
+    $http.get('/rest/clusters').success(function (data) {
+        $scope.caches = data.caches;
+        $scope.spaces = data.spaces;
+        $scope.clusters = data.clusters;
+    });
+
+    $scope.selectItem = function (item) {
+        $scope.selectedItem = item;
+
+        $scope.generateConfig()
+    };
+
+    $scope.generateConfig = function() {
+        var lang = $scope.cfgLang;
+
+        if (lang == 'docker')
+            return;
+
+        var cluster = $scope.selectedItem;
+        
+        if (!cluster)
+            return;
+        
+        $scope.loading = true;
+
+        $http.get('/rest/configGenerator', {params: 
+        {name: cluster.name, lang: lang, generateJavaClass: $scope.generateJavaClass}})
+            .success(
+            function (data) {
+                if (lang == 'java') {
+                    $("<pre class='brush:java' />").text(data).appendTo($('#javaResultDiv').empty());
+                }
+                else if (lang == 'xml') {
+                    $("<pre class='brush:xml' />").text(data).appendTo($('#xmlResultDiv').empty());
+                }
+
+                SyntaxHighlighter.highlight();
+
+                $scope.loading = false;
+            }).error(function (data) {
+                $scope.generateError = "Failed to generate config: " + data;
+
+                $scope.loading = false;
+            });
+    };
+
+    $scope.cfgLang = 'xml';
+
+    $scope.$watch('cfgLang', $scope.generateConfig);
+    $scope.$watch('generateJavaClass', $scope.generateConfig);
+
+    $scope.dockerArg = {};
+    
+    $scope.downloadDocker = function() {
+        var dockerText = $scope.dockerFile();
+        
+        if (dockerText.length == 0)
+            return;
+        
+        var pom = document.createElement('a');
+        pom.setAttribute('href', 'data:application/octet-stream;charset=utf-8,' + encodeURIComponent(dockerText));
+        pom.setAttribute('download', 'Dockerfile');
+
+        pom.style.display = 'none';
+        document.body.appendChild(pom);
+
+        pom.click();
+
+        document.body.removeChild(pom);
+    };
+    
+    $scope.dockerFile = function() {
+        if (!$scope.selectedItem || !$scope.dockerArg) {
+            return '';
+        }
+        
+        var os = $scope.dockerArg.os;
+        if (!os) {
+            os = 'debian:8'
+        }
+        
+        return "" +
+            "# Start from a Debian image.\n"+
+            "FROM " + os + "\n"+
+            "\n"+
+            "# Install tools.\n"+
+            "RUN apt-get update && apt-get install -y --fix-missing \\\n"+
+            "  wget \\\n"+
+            "  dstat \\\n"+
+            "  maven \\\n"+
+            "  git\n"+
+            "\n"+
+            "# Intasll Oracle JDK.\n"+
+            "RUN mkdir /opt/jdk\n"+
+            "\n"+
+            "RUN wget --header \"Cookie: oraclelicense=accept-securebackup-cookie\" \\\n"+
+            "  http://download.oracle.com/otn-pub/java/jdk/7u79-b15/jdk-7u79-linux-x64.tar.gz\n"+
+            "\n"+
+            "RUN tar -zxf jdk-7u79-linux-x64.tar.gz -C /opt/jdk\n"+
+            "\n"+
+            "RUN rm jdk-7u79-linux-x64.tar.gz\n"+
+            "\n"+
+            "RUN update-alternatives --install /usr/bin/java java /opt/jdk/jdk1.7.0_79/bin/java 100\n"+
+            "\n"+
+            "RUN update-alternatives --install /usr/bin/javac javac /opt/jdk/jdk1.7.0_79/bin/javac 100\n"+
+            "\n"+
+            "# Sets java variables.\n"+
+            "ENV JAVA_HOME /opt/jdk/jdk1.7.0_79/\n"+
+            "\n"+
+            "# Create working directory\n"+
+            "WORKDIR /home\n"+
+            "\n"+
+            "RUN wget -O ignite.zip http://tiny.cc/updater/download_ignite.php && unzip ignite.zip && rm ignite.zip\n"+
+            "\n"+
+            "COPY *.xml /tmp/\n"+
+            "\n"+
+            "RUN mv /tmp/*.xml /home/$(ls)/config";
+    };
+}]);

http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/8e792605/modules/web-control-center/nodejs/public/javascripts/dataStructures.js
----------------------------------------------------------------------
diff --git a/modules/web-control-center/nodejs/public/javascripts/dataStructures.js b/modules/web-control-center/nodejs/public/javascripts/dataStructures.js
new file mode 100644
index 0000000..2462708
--- /dev/null
+++ b/modules/web-control-center/nodejs/public/javascripts/dataStructures.js
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+eventGroups = {
+    EVTS_CHECKPOINT: ['EVT_CHECKPOINT_SAVED', 'EVT_CHECKPOINT_LOADED', 'EVT_CHECKPOINT_REMOVED'],
+    EVTS_DEPLOYMENT: ['EVT_CLASS_DEPLOYED', 'EVT_CLASS_UNDEPLOYED', 'EVT_CLASS_DEPLOY_FAILED', 'EVT_TASK_DEPLOYED',
+        'EVT_TASK_UNDEPLOYED', 'EVT_TASK_DEPLOY_FAILED'],
+    EVTS_ERROR: ['EVT_JOB_TIMEDOUT', 'EVT_JOB_FAILED', 'EVT_JOB_FAILED_OVER', 'EVT_JOB_REJECTED', 'EVT_JOB_CANCELLED',
+        'EVT_TASK_TIMEDOUT', 'EVT_TASK_FAILED', 'EVT_CLASS_DEPLOY_FAILED', 'EVT_TASK_DEPLOY_FAILED',
+        'EVT_TASK_DEPLOYED', 'EVT_TASK_UNDEPLOYED', 'EVT_CACHE_REBALANCE_STARTED', 'EVT_CACHE_REBALANCE_STOPPED'],
+    EVTS_DISCOVERY: ['EVT_NODE_JOINED', 'EVT_NODE_LEFT', 'EVT_NODE_FAILED', 'EVT_NODE_SEGMENTED',
+        'EVT_CLIENT_NODE_DISCONNECTED', 'EVT_CLIENT_NODE_RECONNECTED'],
+    EVTS_JOB_EXECUTION: ['EVT_JOB_MAPPED', 'EVT_JOB_RESULTED', 'EVT_JOB_FAILED_OVER', 'EVT_JOB_STARTED',
+        'EVT_JOB_FINISHED', 'EVT_JOB_TIMEDOUT', 'EVT_JOB_REJECTED', 'EVT_JOB_FAILED', 'EVT_JOB_QUEUED',
+        'EVT_JOB_CANCELLED'],
+    EVTS_TASK_EXECUTION: ['EVT_TASK_STARTED', 'EVT_TASK_FINISHED', 'EVT_TASK_FAILED', 'EVT_TASK_TIMEDOUT',
+        'EVT_TASK_SESSION_ATTR_SET', 'EVT_TASK_REDUCED'],
+    EVTS_CACHE: ['EVT_CACHE_ENTRY_CREATED', 'EVT_CACHE_ENTRY_DESTROYED', 'EVT_CACHE_OBJECT_PUT',
+        'EVT_CACHE_OBJECT_READ', 'EVT_CACHE_OBJECT_REMOVED', 'EVT_CACHE_OBJECT_LOCKED', 'EVT_CACHE_OBJECT_UNLOCKED',
+        'EVT_CACHE_OBJECT_SWAPPED', 'EVT_CACHE_OBJECT_UNSWAPPED', 'EVT_CACHE_OBJECT_EXPIRED'],
+    EVTS_CACHE_REBALANCE: ['EVT_CACHE_REBALANCE_STARTED', 'EVT_CACHE_REBALANCE_STOPPED',
+        'EVT_CACHE_REBALANCE_PART_LOADED', 'EVT_CACHE_REBALANCE_PART_UNLOADED', 'EVT_CACHE_REBALANCE_OBJECT_LOADED',
+        'EVT_CACHE_REBALANCE_OBJECT_UNLOADED', 'EVT_CACHE_REBALANCE_PART_DATA_LOST'],
+    EVTS_CACHE_LIFECYCLE: ['EVT_CACHE_STARTED', 'EVT_CACHE_STOPPED', 'EVT_CACHE_NODES_LEFT'],
+    EVTS_CACHE_QUERY: ['EVT_CACHE_QUERY_EXECUTED', 'EVT_CACHE_QUERY_OBJECT_READ'],
+    EVTS_SWAPSPACE: ['EVT_SWAP_SPACE_CLEARED', 'EVT_SWAP_SPACE_DATA_REMOVED', 'EVT_SWAP_SPACE_DATA_READ',
+        'EVT_SWAP_SPACE_DATA_STORED', 'EVT_SWAP_SPACE_DATA_EVICTED'],
+    EVTS_IGFS: ['EVT_IGFS_FILE_CREATED', 'EVT_IGFS_FILE_RENAMED', 'EVT_IGFS_FILE_DELETED', 'EVT_IGFS_FILE_OPENED_READ',
+        'EVT_IGFS_FILE_OPENED_WRITE', 'EVT_IGFS_FILE_CLOSED_WRITE', 'EVT_IGFS_FILE_CLOSED_READ', 'EVT_IGFS_FILE_PURGED',
+        'EVT_IGFS_META_UPDATED', 'EVT_IGFS_DIR_CREATED', 'EVT_IGFS_DIR_RENAMED', 'EVT_IGFS_DIR_DELETED']
+};
+
+jdbcTypes = {
+    BIT: {value: "BIT", code: -7, label: "BIT"},
+    TINYINT: {value: "TINYINT", code: -6, label: "TINYINT"},
+    SMALLINT: {value: "SMALLINT", code: 5, label: "SMALLINT"},
+    INTEGER: {value: "INTEGER", code: 4, label: "INTEGER"},
+    BIGINT: {value: "BIGINT", code: -5, label: "BIGINT"},
+    FLOAT: {value: "FLOAT", code: 6, label: "FLOAT"},
+    REAL: {value: "REAL", code: 7, label: "REAL"},
+    DOUBLE: {value: "DOUBLE", code: 8, label: "DOUBLE"},
+    NUMERIC: {value: "NUMERIC", code: 2, label: "NUMERIC"},
+    DECIMAL: {value: "DECIMAL", code: 3, label: "DECIMAL"},
+    CHAR: {value: "CHAR", code: 1, label: "CHAR"},
+    VARCHAR: {value: "VARCHAR", code: 12, label: "VARCHAR"},
+    DATE: {value: "DATE", code: 91, label: "DATE"},
+    TIME: {value: "TIME", code: 92, label: "TIME"},
+    TIMESTAMP: {value: "TIMESTAMP", code: 93, label: "TIMESTAMP"},
+    BINARY: {value: "BINARY", code: -2, label: "BINARY"}
+};
+
+javaTypes = {
+    INTEGER: {value: "java.lang.Integer", label: "Integer"},
+    LONG: {value: "java.lang.Long", label: "Long"},
+    BIGDECIMAL: {value: "java.math.BigDecimal", label: "BigDecimal"},
+    FLOAT: {value: "java.lang.Float", label: "Float"},
+    DOUBLE: {value: "java.lang.Double", label: "Double"},
+    STRING: {value: "java.lang.String", label: "String"},
+    BOOLEAN: {value: "java.lang.Boolean", label: "Boolean"},
+    BYTE_ARRAY: {value: "byte[]", label: "byte[]"},
+    DATE: {value: "java.sql.Date", label: "Date"},
+    TIME: {value: "java.sql.Time", label: "Time"},
+    TIMESTAMP: {value: "java.sql.Timestamp", label: "Timestamp"}
+};
+
+if (typeof window === 'undefined') {
+    exports.eventGroups = eventGroups;
+    exports.jdbcTypes = jdbcTypes;
+    exports.javaTypes = javaTypes;
+}