incubator-blur-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From amccu...@apache.org
Subject [02/51] [partial] Fixed BLUR-126.
Date Thu, 06 Jun 2013 18:57:34 GMT
http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/blur-util/src/main/java/org/apache/blur/zookeeper/ZkUtils.java
----------------------------------------------------------------------
diff --git a/blur-util/src/main/java/org/apache/blur/zookeeper/ZkUtils.java b/blur-util/src/main/java/org/apache/blur/zookeeper/ZkUtils.java
new file mode 100644
index 0000000..6998814
--- /dev/null
+++ b/blur-util/src/main/java/org/apache/blur/zookeeper/ZkUtils.java
@@ -0,0 +1,199 @@
+package org.apache.blur.zookeeper;
+
+/**
+ * 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.
+ */
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.blur.log.Log;
+import org.apache.blur.log.LogFactory;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.KeeperException.NodeExistsException;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.Watcher.Event.KeeperState;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.data.Stat;
+
+
+/** @author Aaron McCurry (amccurry@nearinfinity.com) */
+public class ZkUtils {
+
+  private final static Log LOG = LogFactory.getLog(ZkUtils.class);
+
+  public static final int ANY_VERSION = -1;
+
+  public static final int DEFAULT_ZK_SESSION_TIMEOUT = 60000;
+
+  public static class ConnectionWatcher implements Watcher {
+
+    private String zkConnectionString;
+    private int sessionTimeout;
+
+    public void setZkConnectionString(String zkConnectionString) {
+      this.zkConnectionString = zkConnectionString;
+    }
+
+    public void setSessionTimeout(int sessionTimeout) {
+      this.sessionTimeout = sessionTimeout;
+    }
+
+    @Override
+    public void process(WatchedEvent event) {
+      KeeperState state = event.getState();
+      LOG.info("ZooKeeper [{0}] timeout [{1}] changed to [{2}] state", zkConnectionString, sessionTimeout, state);
+    }
+
+  }
+
+  public static void pause(Object o) {
+    synchronized (o) {
+      try {
+        o.wait(TimeUnit.SECONDS.toMillis(1));
+      } catch (InterruptedException e) {
+        return;
+      }
+    }
+  }
+  
+  public static ZooKeeper newZooKeeper(String zkConnectionString) throws IOException {
+    int sessionTimeout = DEFAULT_ZK_SESSION_TIMEOUT;
+    ConnectionWatcher watcher = new ConnectionWatcher();
+    watcher.setSessionTimeout(sessionTimeout);
+    watcher.setZkConnectionString(zkConnectionString);
+    return new ZooKeeperClient(zkConnectionString, sessionTimeout, watcher);
+  }
+
+  public static void mkNodesStr(ZooKeeper zk, String path) {
+    if (path == null) {
+      return;
+    }
+    mkNodes(zk, path.split("/"));
+  }
+
+  public static void mkNodes(ZooKeeper zk, String... path) {
+    if (path == null) {
+      return;
+    }
+    for (int i = 0; i < path.length; i++) {
+      StringBuilder builder = new StringBuilder();
+      for (int j = 0; j <= i; j++) {
+        if (!path[j].isEmpty()) {
+          builder.append('/');
+          builder.append(path[j]);
+        }
+      }
+      String pathToCheck = removeDupSeps(builder.toString());
+      if (pathToCheck.isEmpty()) {
+        continue;
+      }
+      try {
+        if (zk.exists(pathToCheck, false) == null) {
+          zk.create(pathToCheck, null, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+        }
+      } catch (NodeExistsException e) {
+        // do nothing
+      } catch (KeeperException e) {
+        LOG.error("error", e);
+        throw new RuntimeException(e);
+      } catch (InterruptedException e) {
+        LOG.error("error", e);
+        throw new RuntimeException(e);
+      }
+    }
+  }
+
+  private static String removeDupSeps(String path) {
+    return path.replace("//", "/");
+  }
+
+  public static String getPath(String... parts) {
+    if (parts == null || parts.length == 0) {
+      return null;
+    }
+    StringBuilder builder = new StringBuilder(parts[0]);
+    for (int i = 1; i < parts.length; i++) {
+      builder.append('/');
+      builder.append(parts[i]);
+    }
+    return builder.toString();
+  }
+
+  public static boolean exists(ZooKeeper zk, String... path) {
+    if (path == null || path.length == 0) {
+      return true;
+    }
+    StringBuilder builder = new StringBuilder(path[0]);
+    for (int i = 1; i < path.length; i++) {
+      builder.append('/').append(path[i]);
+    }
+    try {
+      return zk.exists(builder.toString(), false) != null;
+    } catch (KeeperException e) {
+      throw new RuntimeException(e);
+    } catch (InterruptedException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public static void deleteAnyVersion(ZooKeeper zk, String path) {
+    try {
+      List<String> children = zk.getChildren(path, false);
+      for (String c : children) {
+        deleteAnyVersion(zk, path + "/" + c);
+      }
+      zk.delete(path, ANY_VERSION);
+    } catch (KeeperException e) {
+      if (e.code() == KeeperException.Code.NONODE) {
+        return;
+      }
+      throw new RuntimeException(e);
+    } catch (InterruptedException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  public static void waitUntilExists(ZooKeeper zooKeeper, String path) {
+    final Object o = new Object();
+    try {
+      while (true) {
+        Stat stat = zooKeeper.exists(path, new Watcher() {
+          @Override
+          public void process(WatchedEvent event) {
+            synchronized (o) {
+              o.notifyAll();
+            }
+          }
+        });
+        if (stat == null) {
+          synchronized (o) {
+            o.wait();
+          }
+        } else {
+          return;
+        }
+      }
+    } catch (KeeperException e) {
+      throw new RuntimeException(e);
+    } catch (InterruptedException e) {
+      throw new RuntimeException(e);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/blur-util/src/main/java/org/apache/blur/zookeeper/ZooKeeperClient.java
----------------------------------------------------------------------
diff --git a/blur-util/src/main/java/org/apache/blur/zookeeper/ZooKeeperClient.java b/blur-util/src/main/java/org/apache/blur/zookeeper/ZooKeeperClient.java
new file mode 100644
index 0000000..62ad5c7
--- /dev/null
+++ b/blur-util/src/main/java/org/apache/blur/zookeeper/ZooKeeperClient.java
@@ -0,0 +1,235 @@
+package org.apache.blur.zookeeper;
+
+/**
+ * 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.
+ */
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.blur.log.Log;
+import org.apache.blur.log.LogFactory;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.Op;
+import org.apache.zookeeper.OpResult;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.KeeperException.Code;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+
+public class ZooKeeperClient extends ZooKeeper {
+
+  private static final Log LOG = LogFactory.getLog(ZooKeeperClient.class);
+  private final int internalSessionTimeout;
+
+  public ZooKeeperClient(String connectString, int sessionTimeout, Watcher watcher) throws IOException {
+    super(connectString, sessionTimeout, watcher);
+    internalSessionTimeout = sessionTimeout;
+  }
+
+  public ZooKeeperClient(String connectString, int sessionTimeout, Watcher watcher, boolean canBeReadOnly)
+      throws IOException {
+    super(connectString, sessionTimeout, watcher, canBeReadOnly);
+    internalSessionTimeout = sessionTimeout;
+  }
+
+  public ZooKeeperClient(String connectString, int sessionTimeout, Watcher watcher, long sessionId,
+      byte[] sessionPasswd, boolean canBeReadOnly) throws IOException {
+    super(connectString, sessionTimeout, watcher, sessionId, sessionPasswd, canBeReadOnly);
+    internalSessionTimeout = sessionTimeout;
+  }
+
+  public ZooKeeperClient(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd)
+      throws IOException {
+    super(connectString, sessionTimeout, watcher, sessionId, sessionPasswd);
+    internalSessionTimeout = sessionTimeout;
+  }
+
+  static abstract class ZKExecutor<T> {
+    abstract T execute() throws KeeperException, InterruptedException;
+  }
+
+  public <T> T execute(ZKExecutor<T> executor) throws KeeperException, InterruptedException {
+    final long timestmap = System.currentTimeMillis();
+    int sessionTimeout = getSessionTimeout();
+    if (sessionTimeout == 0) {
+      sessionTimeout = internalSessionTimeout;
+    }
+    while (true) {
+      try {
+        return executor.execute();
+      } catch (KeeperException e) {
+        if (e.code() == Code.CONNECTIONLOSS && timestmap + sessionTimeout >= System.currentTimeMillis()) {
+          LOG.warn("Connection loss");
+          ZkUtils.pause(this);
+          continue;
+        }
+        throw e;
+      }
+    }
+  }
+
+  @Override
+  public String create(final String path, final byte[] data, final List<ACL> acl, final CreateMode createMode)
+      throws KeeperException, InterruptedException {
+    return execute(new ZKExecutor<String>() {
+      @Override
+      String execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.create(path, data, acl, createMode);
+      }
+    });
+  }
+
+  @Override
+  public void delete(final String path, final int version) throws InterruptedException, KeeperException {
+    execute(new ZKExecutor<Void>() {
+      @Override
+      Void execute() throws KeeperException, InterruptedException {
+        ZooKeeperClient.super.delete(path, version);
+        return null;
+      }
+    });
+  }
+
+  @Override
+  public List<OpResult> multi(final Iterable<Op> ops) throws InterruptedException, KeeperException {
+    return execute(new ZKExecutor<List<OpResult>>() {
+      @Override
+      List<OpResult> execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.multi(ops);
+      }
+    });
+  }
+
+  @Override
+  public Stat exists(final String path, final Watcher watcher) throws KeeperException, InterruptedException {
+    return execute(new ZKExecutor<Stat>() {
+      @Override
+      Stat execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.exists(path, watcher);
+      }
+    });
+  }
+
+  @Override
+  public Stat exists(final String path, final boolean watch) throws KeeperException, InterruptedException {
+    return execute(new ZKExecutor<Stat>() {
+      @Override
+      Stat execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.exists(path, watch);
+      }
+    });
+  }
+
+  @Override
+  public byte[] getData(final String path, final Watcher watcher, final Stat stat) throws KeeperException,
+      InterruptedException {
+    return execute(new ZKExecutor<byte[]>() {
+      @Override
+      byte[] execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.getData(path, watcher, stat);
+      }
+    });
+  }
+
+  @Override
+  public byte[] getData(final String path, final boolean watch, final Stat stat) throws KeeperException,
+      InterruptedException {
+    return execute(new ZKExecutor<byte[]>() {
+      @Override
+      byte[] execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.getData(path, watch, stat);
+      }
+    });
+  }
+
+  @Override
+  public Stat setData(final String path, final byte[] data, final int version) throws KeeperException,
+      InterruptedException {
+    return execute(new ZKExecutor<Stat>() {
+      @Override
+      Stat execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.setData(path, data, version);
+      }
+    });
+  }
+
+  @Override
+  public List<ACL> getACL(final String path, final Stat stat) throws KeeperException, InterruptedException {
+    return execute(new ZKExecutor<List<ACL>>() {
+      @Override
+      List<ACL> execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.getACL(path, stat);
+      }
+    });
+  }
+
+  @Override
+  public Stat setACL(final String path, final List<ACL> acl, final int version) throws KeeperException,
+      InterruptedException {
+    return execute(new ZKExecutor<Stat>() {
+      @Override
+      Stat execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.setACL(path, acl, version);
+      }
+    });
+  }
+
+  @Override
+  public List<String> getChildren(final String path, final Watcher watcher) throws KeeperException,
+      InterruptedException {
+    return execute(new ZKExecutor<List<String>>() {
+      @Override
+      List<String> execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.getChildren(path, watcher);
+      }
+    });
+  }
+
+  @Override
+  public List<String> getChildren(final String path, final boolean watch) throws KeeperException, InterruptedException {
+    return execute(new ZKExecutor<List<String>>() {
+      @Override
+      List<String> execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.getChildren(path, watch);
+      }
+    });
+  }
+
+  @Override
+  public List<String> getChildren(final String path, final Watcher watcher, final Stat stat) throws KeeperException,
+      InterruptedException {
+    return execute(new ZKExecutor<List<String>>() {
+      @Override
+      List<String> execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.getChildren(path, watcher, stat);
+      }
+    });
+  }
+
+  @Override
+  public List<String> getChildren(final String path, final boolean watch, final Stat stat) throws KeeperException,
+      InterruptedException {
+    return execute(new ZKExecutor<List<String>>() {
+      @Override
+      List<String> execute() throws KeeperException, InterruptedException {
+        return ZooKeeperClient.super.getChildren(path, watch, stat);
+      }
+    });
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/blur-util/src/main/resources/blur-default.properties
----------------------------------------------------------------------
diff --git a/blur-util/src/main/resources/blur-default.properties b/blur-util/src/main/resources/blur-default.properties
new file mode 100644
index 0000000..6d0a97d
--- /dev/null
+++ b/blur-util/src/main/resources/blur-default.properties
@@ -0,0 +1,63 @@
+# 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.
+
+blur.shard.hostname=
+blur.shard.bind.address=0.0.0.0
+blur.shard.bind.port=40020
+blur.shard.data.fetch.thread.count=8
+blur.shard.server.thrift.thread.count=8
+blur.shard.opener.thread.count=8
+blur.shard.cache.max.querycache.elements=128
+blur.shard.cache.max.timetolive=60000
+blur.shard.filter.cache.class=org.apache.blur.manager.DefaultBlurFilterCache
+blur.shard.index.warmup.class=org.apache.blur.manager.indexserver.DefaultBlurIndexWarmup
+blur.shard.blockcache.direct.memory.allocation=true
+blur.shard.blockcache.slab.count=-1
+blur.shard.buffercache.1024=8192
+blur.shard.buffercache.8192=8192
+blur.shard.safemodedelay=5000
+blur.shard.time.between.commits=30000
+blur.shard.time.between.refreshs=3000
+blur.max.clause.count=1024
+blur.indexmanager.search.thread.count=8
+
+blur.controller.hostname=
+blur.controller.bind.address=0.0.0.0
+blur.controller.bind.port=40010
+blur.controller.server.thrift.thread.count=32
+blur.controller.server.remote.thread.count=64
+blur.controller.remote.fetch.count=100
+blur.controller.cache.max.querycache.elements=128
+blur.controller.cache.max.timetolive=60000
+
+blur.controller.retry.max.fetch.retries=3
+blur.controller.retry.max.mutate.retries=3
+blur.controller.retry.max.default.retries=3
+blur.controller.retry.fetch.delay=500
+blur.controller.retry.mutate.delay=500
+blur.controller.retry.default.delay=500
+blur.controller.retry.max.fetch.delay=2000
+blur.controller.retry.max.mutate.delay=2000
+blur.controller.retry.max.default.delay=2000
+
+blur.query.max.results.fetch=1000
+blur.query.max.row.fetch=100
+blur.query.max.record.fetch=1000
+
+blur.zookeeper.system.time.tolerance=3000
+
+blur.gui.controller.port=40080
+blur.gui.shard.port=40090
+

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/blur-util/src/main/resources/blur-site.properties
----------------------------------------------------------------------
diff --git a/blur-util/src/main/resources/blur-site.properties b/blur-util/src/main/resources/blur-site.properties
new file mode 100644
index 0000000..18ae6f1
--- /dev/null
+++ b/blur-util/src/main/resources/blur-site.properties
@@ -0,0 +1,17 @@
+# 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.
+
+blur.zookeeper.connection=127.0.0.1
+blur.cluster.name=default

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/blur-util/src/main/resources/hadoop-metrics.properties
----------------------------------------------------------------------
diff --git a/blur-util/src/main/resources/hadoop-metrics.properties b/blur-util/src/main/resources/hadoop-metrics.properties
new file mode 100644
index 0000000..dba5756
--- /dev/null
+++ b/blur-util/src/main/resources/hadoop-metrics.properties
@@ -0,0 +1,28 @@
+# Configuration of the "blur" context for null
+blur.class=org.apache.hadoop.metrics.spi.NullContext
+
+# Configuration of the "blur" context for file
+# blur.class=org.apache.hadoop.metrics.file.FileContext
+# blur.period=10
+# blur.fileName=/tmp/blurmetrics.log
+
+# Configuration of the "blur" context for ganglia
+# Pick one: Ganglia 3.0 (former) or Ganglia 3.1 (latter)
+# blur.class=org.apache.hadoop.metrics.ganglia.GangliaContext
+# blur.class=org.apache.hadoop.metrics.ganglia.GangliaContext31
+# blur.period=10
+# blur.servers=localhost:8649
+
+
+# Configuration of the "jvm" context for null
+jvm.class=org.apache.hadoop.metrics.spi.NullContext
+
+# Configuration of the "jvm" context for file
+# jvm.class=org.apache.hadoop.metrics.file.FileContext
+# jvm.period=10
+# jvm.fileName=/tmp/jvmmetrics.log
+
+# Configuration of the "jvm" context for ganglia
+# jvm.class=org.apache.hadoop.metrics.ganglia.GangliaContext
+# jvm.period=10
+# jvm.servers=localhost:8649

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/blur-util/src/test/java/org/apache/blur/zookeeper/ZkUtilsTest.java
----------------------------------------------------------------------
diff --git a/blur-util/src/test/java/org/apache/blur/zookeeper/ZkUtilsTest.java b/blur-util/src/test/java/org/apache/blur/zookeeper/ZkUtilsTest.java
new file mode 100644
index 0000000..157ffa4
--- /dev/null
+++ b/blur-util/src/test/java/org/apache/blur/zookeeper/ZkUtilsTest.java
@@ -0,0 +1,133 @@
+package org.apache.blur.zookeeper;
+
+/**
+ * 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.
+ */
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+
+import org.apache.blur.zookeeper.ZkUtils;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.server.quorum.QuorumPeerMain;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+public class ZkUtilsTest {
+
+  private static final int ANY_VERSION = -1;
+  private static Thread _serverThread;
+  private ZooKeeper _zooKeeper;
+
+  @BeforeClass
+  public static void setupZookeeper() {
+    _serverThread = new Thread(new Runnable() {
+      @Override
+      public void run() {
+        QuorumPeerMain.main(new String[] { "./src/test/resources/zoo.cfg" });
+      }
+    });
+    _serverThread.setDaemon(true);
+    _serverThread.start();
+  }
+
+  @Before
+  public void setUp() throws IOException, InterruptedException {
+    final Object lock = new Object();
+    synchronized (lock) {
+      _zooKeeper = new ZooKeeper("127.0.0.1:10101", 10000, new Watcher() {
+        @Override
+        public void process(WatchedEvent event) {
+          synchronized (lock) {
+            lock.notifyAll();
+          }
+        }
+      });
+      lock.wait();
+    }
+
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    delete("/test/foo/bar");
+    delete("/test/foo");
+    delete("/test");
+    _zooKeeper.close();
+  }
+
+  @Test
+  public void testMkNodesStrWhenNoNodesExist() throws Exception {
+    assertDoesNotExist("/test/foo/bar");
+    ZkUtils.mkNodesStr(_zooKeeper, "/test/foo/bar");
+    assertExists("/test/foo/bar");
+  }
+
+  @Test
+  public void testMkNodesStrWhenSomeNodesExist() throws Exception {
+    create("/test");
+    create("/test/foo");
+    assertThat(_zooKeeper.exists("/test/foo/bar", false), is(nullValue()));
+    ZkUtils.mkNodesStr(_zooKeeper, "/test/foo/bar");
+    assertExists("/test/foo/bar");
+  }
+
+  @Test
+  public void testMkNodesWhenNoNodesExist() throws Exception {
+    assertDoesNotExist("/test/foo/bar");
+    ZkUtils.mkNodes(_zooKeeper, "test", "foo", "bar");
+    assertExists("/test/foo/bar");
+  }
+
+  @Test
+  public void testMkNodesWhenSomeNodesExist() throws Exception {
+    create("/test");
+    create("/test/foo");
+    assertThat(_zooKeeper.exists("/test/foo/bar", false), is(nullValue()));
+    ZkUtils.mkNodes(_zooKeeper, "/test", "foo", "bar");
+    assertExists("/test/foo/bar");
+  }
+
+  private void create(String path) throws KeeperException, InterruptedException {
+    _zooKeeper.create(path, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+  }
+
+  private void delete(String path) throws KeeperException, InterruptedException {
+    if (_zooKeeper.exists(path, false) != null) {
+      _zooKeeper.delete(path, ANY_VERSION);
+    }
+  }
+
+  private void assertDoesNotExist(String path) throws InterruptedException, KeeperException {
+    assertThat(_zooKeeper.exists(path, false), is(nullValue()));
+  }
+
+  private void assertExists(String path) throws InterruptedException, KeeperException {
+    assertThat(_zooKeeper.exists(path, false), is(notNullValue()));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/blur-util/src/test/resources/zoo.cfg
----------------------------------------------------------------------
diff --git a/blur-util/src/test/resources/zoo.cfg b/blur-util/src/test/resources/zoo.cfg
new file mode 100644
index 0000000..d29bf3b
--- /dev/null
+++ b/blur-util/src/test/resources/zoo.cfg
@@ -0,0 +1,12 @@
+# The number of milliseconds of each tick
+tickTime=2000
+# The number of ticks that the initial 
+# synchronization phase can take
+initLimit=10
+# The number of ticks that can pass between 
+# sending a request and getting an acknowledgement
+syncLimit=5
+# the directory where the snapshot is stored.
+dataDir=/tmp/test-zk-tmp
+# the port at which the clients will connect
+clientPort=10101
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/README
----------------------------------------------------------------------
diff --git a/contrib/blur-console/README b/contrib/blur-console/README
new file mode 100644
index 0000000..cbf515b
--- /dev/null
+++ b/contrib/blur-console/README
@@ -0,0 +1,8 @@
+The following tools are included in this repository:
+
+Console:
+    rails application
+        - The admin rails application for administering a Blur instance
+
+    agent
+        - Java agent that collects various information for the admin console

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/.gitignore
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/.gitignore b/contrib/blur-console/blur-admin/.gitignore
new file mode 100644
index 0000000..a1c95d0
--- /dev/null
+++ b/contrib/blur-console/blur-admin/.gitignore
@@ -0,0 +1,23 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+#
+# If you find yourself ignoring temporary files generated by your text editor
+# or operating system, you probably want to add a global ignore instead:
+#   git config --global core.excludesfile ~/.gitignore_global
+
+# Ignore bundler config
+/.bundle
+
+# Ignore the default SQLite database.
+/db/*.sqlite3
+
+# Ignore all logfiles and tempfiles.
+/log/*.log
+/tmp
+
+# Ignore compiled assets
+/public/assets/*
+/app/assets/javascripts/routes.js
+.sass-cache
+
+#ignore rubymine
+/.idea

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/Capfile
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/Capfile b/contrib/blur-console/blur-admin/Capfile
new file mode 100644
index 0000000..39258ef
--- /dev/null
+++ b/contrib/blur-console/blur-admin/Capfile
@@ -0,0 +1,33 @@
+require 'capistrano/recipes/deploy/strategy/remote_cache'
+
+class RemoteCacheSubdir < Capistrano::Deploy::Strategy::RemoteCache
+
+  private
+
+  def repository_cache_subdir
+    if configuration[:deploy_subdir] then
+      File.join(repository_cache, configuration[:deploy_subdir])
+    else
+      repository_cache
+    end
+  end
+
+  def copy_repository_cache
+    logger.trace "copying the cached version to #{configuration[:release_path]}"
+    if copy_exclude.empty? 
+      run "cp -RPp #{repository_cache_subdir} #{configuration[:release_path]} && #{mark}"
+    else
+      exclusions = copy_exclude.map { |e| "--exclude=\"#{e}\"" }.join(' ')
+      run "rsync -lrpt #{exclusions} #{repository_cache_subdir}/* #{configuration[:release_path]} && #{mark}"
+    end
+  end
+
+end
+
+
+set :strategy, RemoteCacheSubdir.new(self)
+
+load 'deploy' if respond_to?(:namespace) # cap2 differentiator
+Dir['vendor/gems/*/recipes/*.rb','vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }
+
+load 'config/deploy' # remove this line to skip loading any of the default tasks
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/Gemfile
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/Gemfile b/contrib/blur-console/blur-admin/Gemfile
new file mode 100644
index 0000000..c9c4f16
--- /dev/null
+++ b/contrib/blur-console/blur-admin/Gemfile
@@ -0,0 +1,39 @@
+source 'https://rubygems.org'
+
+gem 'rails', '3.2.2'
+
+gem 'mysql2'
+gem 'jquery-rails'
+gem 'thrift_client'
+gem 'authlogic'
+gem 'cancan', '2.0.0.alpha', :path => 'vendor/gems/cancan'
+gem 'twitter_bootstrap_form_for'
+gem 'bootstrap-sass'
+gem 'js-routes'
+gem 'haml-rails'
+gem 'sass-rails',   '~> 3.2.3'
+gem 'ejs'
+
+# Gems used only for assets and not required
+# in production environments by default.
+group :assets do
+  gem 'coffee-rails', '~> 3.2.1'
+  gem 'uglifier', '>= 1.0.3'
+end
+
+group :development, :test do
+  gem 'rspec-rails'
+  gem 'capybara'
+  gem 'capybara-webkit'
+  gem 'ci_reporter'
+  gem 'rails-erd'
+  gem 'factory_girl_rails'
+  gem 'launchy'
+  gem 'thin'
+  gem 'debugger'
+end
+
+group :test do
+  gem 'simplecov', :require => false
+end
+

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/Gemfile.lock
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/Gemfile.lock b/contrib/blur-console/blur-admin/Gemfile.lock
new file mode 100644
index 0000000..83e00ae
--- /dev/null
+++ b/contrib/blur-console/blur-admin/Gemfile.lock
@@ -0,0 +1,222 @@
+PATH
+  remote: vendor/gems/cancan
+  specs:
+    cancan (2.0.0.alpha)
+
+GEM
+  remote: https://rubygems.org/
+  specs:
+    actionmailer (3.2.2)
+      actionpack (= 3.2.2)
+      mail (~> 2.4.0)
+    actionpack (3.2.2)
+      activemodel (= 3.2.2)
+      activesupport (= 3.2.2)
+      builder (~> 3.0.0)
+      erubis (~> 2.7.0)
+      journey (~> 1.0.1)
+      rack (~> 1.4.0)
+      rack-cache (~> 1.1)
+      rack-test (~> 0.6.1)
+      sprockets (~> 2.1.2)
+    activemodel (3.2.2)
+      activesupport (= 3.2.2)
+      builder (~> 3.0.0)
+    activerecord (3.2.2)
+      activemodel (= 3.2.2)
+      activesupport (= 3.2.2)
+      arel (~> 3.0.2)
+      tzinfo (~> 0.3.29)
+    activeresource (3.2.2)
+      activemodel (= 3.2.2)
+      activesupport (= 3.2.2)
+    activesupport (3.2.2)
+      i18n (~> 0.6)
+      multi_json (~> 1.0)
+    addressable (2.2.7)
+    arel (3.0.2)
+    authlogic (3.1.0)
+      activerecord (>= 3.0.7)
+      activerecord (>= 3.0.7)
+    bootstrap-sass (2.0.1)
+    builder (3.0.0)
+    capybara (1.1.2)
+      mime-types (>= 1.16)
+      nokogiri (>= 1.3.3)
+      rack (>= 1.0.0)
+      rack-test (>= 0.5.4)
+      selenium-webdriver (~> 2.0)
+      xpath (~> 0.1.4)
+    capybara-webkit (0.12.1)
+      capybara (>= 1.0.0, < 1.2)
+      json
+    childprocess (0.3.1)
+      ffi (~> 1.0.6)
+    ci_reporter (1.7.0)
+      builder (>= 2.1.2)
+    coffee-rails (3.2.2)
+      coffee-script (>= 2.2.0)
+      railties (~> 3.2.0)
+    coffee-script (2.2.0)
+      coffee-script-source
+      execjs
+    coffee-script-source (1.2.0)
+    columnize (0.3.6)
+    daemons (1.1.9)
+    debugger (1.2.0)
+      columnize (>= 0.3.1)
+      debugger-linecache (~> 1.1.1)
+      debugger-ruby_core_source (~> 1.1.3)
+    debugger-linecache (1.1.2)
+      debugger-ruby_core_source (>= 1.1.1)
+    debugger-ruby_core_source (1.1.3)
+    diff-lcs (1.1.3)
+    ejs (1.0.0)
+    erubis (2.7.0)
+    eventmachine (0.12.10)
+    execjs (1.3.0)
+      multi_json (~> 1.0)
+    factory_girl (2.6.0)
+      activesupport (>= 2.3.9)
+    factory_girl_rails (1.7.0)
+      factory_girl (~> 2.6.0)
+      railties (>= 3.0.0)
+    ffi (1.0.11)
+    haml (3.1.4)
+    haml-rails (0.3.4)
+      actionpack (~> 3.0)
+      activesupport (~> 3.0)
+      haml (~> 3.0)
+      railties (~> 3.0)
+    hike (1.2.1)
+    i18n (0.6.0)
+    journey (1.0.3)
+    jquery-rails (2.0.1)
+      railties (>= 3.2.0, < 5.0)
+      thor (~> 0.14)
+    js-routes (0.7.5)
+      rails (>= 3.0)
+    json (1.6.5)
+    launchy (2.0.5)
+      addressable (~> 2.2.6)
+    mail (2.4.1)
+      i18n (>= 0.4.0)
+      mime-types (~> 1.16)
+      treetop (~> 1.4.8)
+    mime-types (1.17.2)
+    multi_json (1.3.6)
+    mysql2 (0.3.11)
+    nokogiri (1.5.0)
+    polyglot (0.3.3)
+    rack (1.4.1)
+    rack-cache (1.1)
+      rack (>= 0.4)
+    rack-ssl (1.3.2)
+      rack
+    rack-test (0.6.1)
+      rack (>= 1.0)
+    rails (3.2.2)
+      actionmailer (= 3.2.2)
+      actionpack (= 3.2.2)
+      activerecord (= 3.2.2)
+      activeresource (= 3.2.2)
+      activesupport (= 3.2.2)
+      bundler (~> 1.0)
+      railties (= 3.2.2)
+    rails-erd (0.4.5)
+      activerecord (~> 3.0)
+      activesupport (~> 3.0)
+      ruby-graphviz (~> 0.9.18)
+    railties (3.2.2)
+      actionpack (= 3.2.2)
+      activesupport (= 3.2.2)
+      rack-ssl (~> 1.3.2)
+      rake (>= 0.8.7)
+      rdoc (~> 3.4)
+      thor (~> 0.14.6)
+    rake (0.9.2.2)
+    rdoc (3.12)
+      json (~> 1.4)
+    rspec (2.8.0)
+      rspec-core (~> 2.8.0)
+      rspec-expectations (~> 2.8.0)
+      rspec-mocks (~> 2.8.0)
+    rspec-core (2.8.0)
+    rspec-expectations (2.8.0)
+      diff-lcs (~> 1.1.2)
+    rspec-mocks (2.8.0)
+    rspec-rails (2.8.1)
+      actionpack (>= 3.0)
+      activesupport (>= 3.0)
+      railties (>= 3.0)
+      rspec (~> 2.8.0)
+    ruby-graphviz (0.9.21)
+    rubyzip (0.9.6.1)
+    sass (3.1.15)
+    sass-rails (3.2.4)
+      railties (~> 3.2.0)
+      sass (>= 3.1.10)
+      tilt (~> 1.3)
+    selenium-webdriver (2.20.0)
+      childprocess (>= 0.2.5)
+      ffi (~> 1.0)
+      multi_json (~> 1.0)
+      rubyzip
+    simplecov (0.6.4)
+      multi_json (~> 1.0)
+      simplecov-html (~> 0.5.3)
+    simplecov-html (0.5.3)
+    sprockets (2.1.2)
+      hike (~> 1.2)
+      rack (~> 1.0)
+      tilt (~> 1.1, != 1.3.0)
+    thin (1.4.1)
+      daemons (>= 1.0.9)
+      eventmachine (>= 0.12.6)
+      rack (>= 1.0.0)
+    thor (0.14.6)
+    thrift (0.8.0)
+    thrift_client (0.8.1)
+      thrift (~> 0.8.0)
+    tilt (1.3.3)
+    treetop (1.4.10)
+      polyglot
+      polyglot (>= 0.3.1)
+    twitter_bootstrap_form_for (1.0.5)
+      actionpack (~> 3)
+      railties (~> 3)
+    tzinfo (0.3.31)
+    uglifier (1.2.3)
+      execjs (>= 0.3.0)
+      multi_json (>= 1.0.2)
+    xpath (0.1.4)
+      nokogiri (~> 1.3)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  authlogic
+  bootstrap-sass
+  cancan (= 2.0.0.alpha)!
+  capybara
+  capybara-webkit
+  ci_reporter
+  coffee-rails (~> 3.2.1)
+  debugger
+  ejs
+  factory_girl_rails
+  haml-rails
+  jquery-rails
+  js-routes
+  launchy
+  mysql2
+  rails (= 3.2.2)
+  rails-erd
+  rspec-rails
+  sass-rails (~> 3.2.3)
+  simplecov
+  thin
+  thrift_client
+  twitter_bootstrap_form_for
+  uglifier (>= 1.0.3)

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/Rakefile
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/Rakefile b/contrib/blur-console/blur-admin/Rakefile
new file mode 100644
index 0000000..8427d1e
--- /dev/null
+++ b/contrib/blur-console/blur-admin/Rakefile
@@ -0,0 +1,21 @@
+#!/usr/bin/env rake
+# Add your own tasks in files placed in lib/tasks ending in .rake,
+# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
+
+require File.expand_path('../config/application', __FILE__)
+
+module ::BlurAdmin
+  class Application
+    include Rake::DSL
+  end
+end
+
+module ::RakeFileUtils
+  extend Rake::FileUtilsExt
+end
+
+if Rails.env == 'development' or Rails.env == 'test'
+  require 'ci/reporter/rake/rspec'
+end
+
+BlurAdmin::Application.load_tasks

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/Regression.md
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/Regression.md b/contrib/blur-console/blur-admin/Regression.md
new file mode 100644
index 0000000..d53e8d5
--- /dev/null
+++ b/contrib/blur-console/blur-admin/Regression.md
@@ -0,0 +1,90 @@
+# Regression Testing
+## Steps needed to manually regression test BlurConsole:
+
+### Dashboard
+  1. Click on a zookeeper, this should take you to that zookeepers environment page and set it as the current zookeeper.
+  2. Click on an hdfs, this should take you to the hdfs page and open up the root of the selected hdfs instance.
+  3. Change the state of a few of the shards and controllers, make sure that the zookeeper widgets update properly. This includes the icon states and the number of online and offline shards/controllers.
+  4. Change the state of the hdfs, make sure that the hdfs widgets update properly.
+  5. Start a new session, and click on the Environment header link, this should prompt a popup asking for a Zookeeper to set as the current.
+
+  Last Completed: 1/7/2013
+
+### Environment Page
+  1. Ensure that the values in the tables track the state of the nodes (similar to the Dashboard page). Change values and ensure that the values in the tables update.
+  2. Change the zookeeper status and ensure that the header changes color and text properly.
+  3. Set the zookeeper, controller's, and shard's status to offline. While offline they should have an 'x' icon that will 'forget' the nodes. Ensure that this is true and that by clicking each one the item is removed.
+
+  Last Completed: 1/7/2013
+
+### Blur Tables Page
+  1. Enable, disable, and delete tables. Ensure that the tables move into their intermediary states and then eventually reach their end state.
+    * Try this with tables that have been queried recently
+    * Try this with the check all and multi-check functionality
+  2. View the hosts and shards, ensure that the values are accurate.
+  3. Click on the view column and ensure that the table metadata is consistent.
+    * Also while inside this popup test that the terms search ability still works.
+  4. Comment on a table and ensure that the comment is preserved during table state changes.
+
+
+### Blur Queries Page
+  1. Run, or create, a few queries and ensure that they appear in the queries table.
+  2. Set the "Queries in the last" option to the different values and ensure that they are being hidden / shown properly.
+  3. Set the "Quick filter" to the different values and ensure that the queries are being properly filtered.
+  4. Set the "Auto Refresh" to the different values and ensure that the server is being queried at the new interval.
+  5. Test a few different filters in the "Filter Queries" box, be sure to test the hidden column state.
+  6. Test that the columns sort properly when sorting by each header.
+
+  Last Completed: 1/7/2013
+
+### Search Page
+  1. Run a query and ensure that it is returning results.
+  2. Change the table and test to see that it changes the filters in the advanced tab.
+  3. Advanced Tab
+    * Test that the column and column family filters work properly.
+    * Test that the search on and return functions limit each other and request / return the proper values.
+    * Test that start and fetch alter the returned results.
+    * Test the pre and post filters.
+  4. Saved Tab
+    * Test creating a new saved search
+    * Test loading a saved search
+    * Test updating a saved search
+    * Test deleting a saved search
+
+  Last Completed: 1/7/2013
+
+### Hdfs Page
+  1. Traverse the file system looking at top level hdfs instances, folders, and files.
+  2. Right click at each level to see the changes in the options.
+  3. Test each of the menu options at each level.
+  4. Test that the radial graph works and can be navigated.
+
+  Last Completed: 1/7/2013
+
+### Hdfs Metrics Page
+  1. Hover over the data points and check that the labels are displaying the proper values.
+  2. Change the date range using the calendar popup and the sliders, ensure that the range is changing properly.
+  3. Check that the above tests pass for all tabs.
+
+  Last Completed: 1/7/2013
+
+### Audits Page
+  1. Perform a few actions that will cause an audit to be generated, then check that the audit is displayed in the audit table.
+  2. Change the date range and ensure that the audits outside the range are removed from the list.
+  3. Enter a few different search terms into the "Filter Audits" box and test that it is properly filtering the audits.
+
+  Last Completed: 1/7/2013
+
+### Admin Page
+  1. Test that an admin can edit a user.
+  2. Test that an admin can create a user.
+  3. Test that an admin can delete a user.
+  3. Test that an admin can change Table Filter regex.
+
+  Last Completed: 1/7/2013
+
+### Account Page
+  1. Test that a user can choose column family preferences, also ensure that these preferences are being enforced on the search page.
+  2. Test that a user can choose a zookeeper preference, also ensure that this preference is being enforced on the initial navigation of the app.
+  3. Test that a user can edit themselves, and not others.
+

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/404_file_not_found.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/404_file_not_found.png b/contrib/blur-console/blur-admin/app/assets/images/404_file_not_found.png
new file mode 100644
index 0000000..e7b3ad3
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/404_file_not_found.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/arrow_down.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/arrow_down.png b/contrib/blur-console/blur-admin/app/assets/images/arrow_down.png
new file mode 100644
index 0000000..00366c7
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/arrow_down.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/arrow_up.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/arrow_up.png b/contrib/blur-console/blur-admin/app/assets/images/arrow_up.png
new file mode 100644
index 0000000..e4d764a
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/arrow_up.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/blur_logo.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/blur_logo.png b/contrib/blur-console/blur-admin/app/assets/images/blur_logo.png
new file mode 100644
index 0000000..520ce8f
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/blur_logo.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/database16.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/database16.png b/contrib/blur-console/blur-admin/app/assets/images/database16.png
new file mode 100644
index 0000000..f60d8c5
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/database16.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/expand_folder.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/expand_folder.png b/contrib/blur-console/blur-admin/app/assets/images/expand_folder.png
new file mode 100644
index 0000000..6b860d9
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/expand_folder.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/failure.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/failure.png b/contrib/blur-console/blur-admin/app/assets/images/failure.png
new file mode 100644
index 0000000..3c47544
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/failure.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/favicon.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/favicon.png b/contrib/blur-console/blur-admin/app/assets/images/favicon.png
new file mode 100644
index 0000000..fa799f9
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/favicon.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/file16.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/file16.png b/contrib/blur-console/blur-admin/app/assets/images/file16.png
new file mode 100644
index 0000000..d6e7119
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/file16.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/folder16.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/folder16.png b/contrib/blur-console/blur-admin/app/assets/images/folder16.png
new file mode 100644
index 0000000..b6c5bb0
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/folder16.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/gradient.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/gradient.png b/contrib/blur-console/blur-admin/app/assets/images/gradient.png
new file mode 100644
index 0000000..535d664
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/gradient.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/green_light.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/green_light.png b/contrib/blur-console/blur-admin/app/assets/images/green_light.png
new file mode 100644
index 0000000..7e92315
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/green_light.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/icons.gif
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/icons.gif b/contrib/blur-console/blur-admin/app/assets/images/icons.gif
new file mode 100644
index 0000000..a58eb93
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/icons.gif differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/loader.gif
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/loader.gif b/contrib/blur-console/blur-admin/app/assets/images/loader.gif
new file mode 100644
index 0000000..1845e7d
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/loader.gif differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/offline.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/offline.png b/contrib/blur-console/blur-admin/app/assets/images/offline.png
new file mode 100644
index 0000000..2c9d655
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/offline.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/online.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/online.png b/contrib/blur-console/blur-admin/app/assets/images/online.png
new file mode 100644
index 0000000..beecde3
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/online.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/red_light.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/red_light.png b/contrib/blur-console/blur-admin/app/assets/images/red_light.png
new file mode 100644
index 0000000..3a6306c
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/red_light.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/server_error.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/server_error.png b/contrib/blur-console/blur-admin/app/assets/images/server_error.png
new file mode 100644
index 0000000..fa06893
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/server_error.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/vline.gif
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/vline.gif b/contrib/blur-console/blur-admin/app/assets/images/vline.gif
new file mode 100644
index 0000000..1b00ae5
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/vline.gif differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/images/warning.png
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/images/warning.png b/contrib/blur-console/blur-admin/app/assets/images/warning.png
new file mode 100644
index 0000000..df58c97
Binary files /dev/null and b/contrib/blur-console/blur-admin/app/assets/images/warning.png differ

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/javascripts/admin.js
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/javascripts/admin.js b/contrib/blur-console/blur-admin/app/assets/javascripts/admin.js
new file mode 100644
index 0000000..4ca3ef6
--- /dev/null
+++ b/contrib/blur-console/blur-admin/app/assets/javascripts/admin.js
@@ -0,0 +1,11 @@
+$(document).ready(function(){
+  $('.table-filter').on('ajax:submit', function(){
+    $(this).find('.control-group').addClass('info');
+  });
+  $('.table-filter').on('ajax:success', function(){
+    $(this).find('.control-group').addClass('success');
+  });
+  $('.table-filter').on('ajax:error', function(){
+    $(this).find('.control-group').addClass('error');
+  });
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/javascripts/application.js
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/javascripts/application.js b/contrib/blur-console/blur-admin/app/assets/javascripts/application.js
new file mode 100644
index 0000000..c67ad30
--- /dev/null
+++ b/contrib/blur-console/blur-admin/app/assets/javascripts/application.js
@@ -0,0 +1,79 @@
+//= require jquery
+//= require jquery-ui
+//= require jquery_ujs
+//= require modernizr
+//= require placeholder
+//= require bootstrap
+//= require bootstrap-modal-helper
+//= require templates
+//= require underscore
+//= require backbone/backbone
+//= require backbone/backbone-extension
+//= require flash_message
+//= require_self
+
+$(document).ready(function(){
+  // Global variable for the spinner (makes displaying the spinner simpler)
+  window.Spinner = $('<img id="loading-spinner" alt="Loading..." src="/assets/loader.gif"/>')
+  // Global display message function
+
+  // Determines the help tab that needs to be opened
+  $('#page-help').click(function(){
+    var url = window.location.pathname;
+    var tab;
+    if (url === '/' || url === 'zookeepers') {
+      tab = "dashboard";
+    } else if (url.substring(1) == 'users') {
+      tab = "admin";
+    } else if (url.match(/hdfs(?!_)/)){
+      tab = "hdfs";
+    } else {
+      var pieces = url.substring(1).split('/');
+      if (pieces.length <= 2) {
+        tab = pieces[0];
+      } else {
+        tab = pieces[2];
+      }
+    }
+    window.open(Routes.help_path(tab), "Help Menu","menubar=0,resizable=0,width=500,height=600");
+    return false;
+  });
+
+  if (typeof Zookeeper !== 'undefined' && Zookeeper.instances){
+    $('#env_link, #tables_link, #queries_link, #search_link').click( function(evt){
+      var self = this;
+      if (Zookeeper.instances.length === 0){
+        alert('There are no Zookeeper Instances registered yet.  This page will not work until then.');
+        return false;
+      } else if (Zookeeper.instances.length === 1 || CurrentZookeeper !== null){
+        return;
+      } else {
+        var select_box = "<div style='text-align:center'><select id='zookeeper_selector' style='font-size: 20px'><option value=''></option>";
+        $.each(Zookeeper.instances, function(){
+          select_box += "<option value='" + this.id + "'>" + this.name + "</option>";
+        });
+        select_box += "</select></div>";
+        $().popup({
+          body: select_box,
+          title: 'Select a Zookeeper Instance to use:',
+          shown: function(){
+            $('#zookeeper_selector').change(function(){
+              window.location = window.location.protocol + '//' + window.location.host + '/zookeepers/' + $(this).val() + ($(self).attr('data-url-extension') || '');
+            });
+          }
+        });
+        return false;
+      }
+    });
+  }
+
+  $('#zookeeper_id').change(function(){
+    if (window.location.href.match(/(zookeepers\/)\d/)){
+      window.location = window.location.href.replace(/(zookeepers\/)\d/, '$1' + $(this).val());
+    } else {
+      window.location = '/zookeepers/' + + $(this).val();
+    }
+  });
+
+  $('.dropdown-toggle').dropdown();
+});

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/javascripts/audits.js
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/javascripts/audits.js b/contrib/blur-console/blur-admin/app/assets/javascripts/audits.js
new file mode 100644
index 0000000..6d28bf7
--- /dev/null
+++ b/contrib/blur-console/blur-admin/app/assets/javascripts/audits.js
@@ -0,0 +1,112 @@
+//= require jquery.dataTables
+//= require jquery.timepicker
+//= require datatables.fnReloadAjax
+//= require_self
+
+$.extend( $.fn.dataTableExt.oStdClasses, {
+  "sSortAsc": "header headerSortDown",
+  "sSortDesc": "header headerSortUp",
+  "sSortable": "header"
+});
+
+$(document).ready(function() {
+  //Sets max time to one hour after page load to fix button with Now button not going to current time
+  var adjust_time = function(date){
+    date.setHours(date.getHours()+1);
+    return date;
+  };
+  var setup_datepickers = function(){
+    var default_timepicker_options = {
+      showMinute: false,
+      maxDate: adjust_time(new Date),
+      hourGrid: 6,
+      ampm: true,
+    };
+
+    var from_now = new Date();
+    var from_hours = urlVars['from']
+    from_now.setMinutes(0);
+
+
+
+    var to_now = new Date();
+    var to_hours = urlVars['to']
+    to_now.setMinutes(0);
+
+    var from_input = $('<input class="from-cal" placeholder="from"/>').datetimepicker(default_timepicker_options);
+    var to_input = $('<input class="to-cal" placeholder="to"/>').datetimepicker(default_timepicker_options);
+
+    if (from_hours){
+      from_now.setHours(from_now.getHours() - from_hours);
+      from_input.datepicker('setDate', from_now);
+    }
+
+    if (to_hours){
+      to_now.setHours(to_now.getHours() - to_hours);
+      to_input.datepicker('setDate', to_now);
+    }
+
+    $('.row > .span2').prepend('<label>Audit range:</label>', from_input, to_input ,'<button class="btn refresh-button">Refresh</button>');
+  };
+  //Grabs the current time from the page elements (long number string in element id's) 
+  var urlVars = function() {
+    var vars = {};
+    var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, key, value) {
+      vars[key] = value;
+    });
+    return vars;
+  }();
+
+  var columnDefinitions = [
+    {mData: 'action', bSortable : false},
+    {mData: 'zookeeper_affected'},
+    {mData: 'username', bVisible : false, bSortable : false},
+    {mData: 'user'},
+    {mData: 'model'},
+    {mData: 'mutation'},
+    {mData: 'date_audited', asSorting: ['desc', 'asc']}
+  ];
+
+  var audit_data_table = $('#audits_table > table').dataTable({
+      sDom: "<'row'<'span4'i><'span8'><'span3'f><'span2'r>>t",
+      bPaginate: false,
+      bProcessing: true,
+      bAutoWidth: false,
+      bDeferRender: true,
+      aoColumns: columnDefinitions,
+      oLanguage: {
+        sInfoEmpty: "",
+        sInfo: "Displaying _TOTAL_ audits",
+        sSearch: "Filter audits:",
+        sZeroRecords: "No audits to display",
+        sInfoFiltered: "(filtered from _MAX_ total audits)"
+      }
+  });
+  //On Page Load
+  audit_data_table.fnSort([[6, 'desc']]);
+
+  setup_datepickers();
+  //Page Listeners
+  $('.refresh-button').on('click', function(){
+    var now = new Date();
+    var from = Math.floor((now - $('.from-cal').datetimepicker('getDate')) / 3600 / 1000);
+    var to = Math.floor((now - $('.to-cal').datetimepicker('getDate')) / 3600 / 1000);
+    var params = '?from=' + from + '&to=' + to;
+    var full_url = window.location.protocol + '//' + window.location.host + window.location.pathname + params;
+
+    if (!Modernizr.history) {
+      window.location(full_url);
+    } else {
+      audit_data_table.fnReloadAjax(Routes.audits_path({format: 'json'}) + params);
+
+      if(location.search.length === 0){
+        history.replaceState(null, "Audits | Blur Console", full_url);
+      } else if(location.search !== full_url){
+        history.pushState(null, "Search | Blur Console", full_url);
+      }
+    }
+  });
+});
+
+
+

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/javascripts/blur_queries.js
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/javascripts/blur_queries.js b/contrib/blur-console/blur-admin/app/assets/javascripts/blur_queries.js
new file mode 100644
index 0000000..fd5918e
--- /dev/null
+++ b/contrib/blur-console/blur-admin/app/assets/javascripts/blur_queries.js
@@ -0,0 +1,197 @@
+//= require jquery.dataTables
+//= require datatables.fnReloadAjax
+//= require_self
+
+$.extend($.fn.dataTableExt.oStdClasses, {
+  "sSortAsc": "header headerSortDown",
+  "sSortDesc": "header headerSortUp",
+  "sSortable": "header"
+});
+
+$(document).ready(function() {
+  var visible_column_count = $('#queries-table thead th').length;
+  var refresh_rate = -1;
+  var refresh_timeout = null;
+  var data_table = null;
+  var load_queries = function() {
+    data_table = $('#queries-table').dataTable({
+      "sDom": "<'row'<'span4'i><'span2'r><'span3'f>>t",
+      bPaginate: false,
+      bProcessing: true,
+      bAutoWidth: false,
+      bDeferRender: true,
+      "oLanguage": {
+        "sInfoEmpty": "",
+        "sInfo": "Displaying _TOTAL_ queries",
+        "sSearch": "Filter queries:",
+        "sZeroRecords": "No queries to display",
+        "sInfoFiltered": "(filtered from _MAX_ total queries)"
+      },
+
+      sAjaxSource: Routes.refresh_zookeeper_blur_queries_path(CurrentZookeeper, 1, {format: 'json'}),
+      aoColumns: table_cols(),
+      fnRowCallback: process_row
+    });
+    data_table.fnSort([[4, 'desc']]);
+    add_refresh_rates(data_table);
+    $('#queries-table').ajaxComplete(function(e, xhr, settings) {
+      if (settings.url.indexOf('/blur_queries/refresh') >= 0) {
+        if (refresh_rate > -1) {
+          refresh_timeout = setTimeout(function() {
+            var range_time_limit = $('.time_range').find('option:selected').val();
+            data_table.fnReloadAjax(Routes.refresh_zookeeper_blur_queries_path(CurrentZookeeper, range_time_limit, {format: 'json'}));
+          }, refresh_rate * 1000);
+        }
+      }
+    });
+    $('.time_range').live('change', function() {
+      var range_time_limit = $(this).find('option:selected').val();
+      data_table.fnReloadAjax(Routes.refresh_zookeeper_blur_queries_path(CurrentZookeeper, range_time_limit, {format: 'json'}));
+    });
+    $('.filter_option').on('click', function(){
+      var container = $(this);
+      var index = visible_column_count - 2;
+      var filter_string = container.attr("data-filter");
+      $('#queries-table').dataTable().fnFilter(filter_string, index);
+    })
+  };
+
+  var table_cols = function() {
+    if (visible_column_count === 8) {
+      return [
+        {
+          "mDataProp": "userid",
+					"sWidth": "85px"
+        }, {
+          "mDataProp": "query",
+          "sWidth": "500px"
+        }, {
+          "mDataProp": "tablename",
+					"sWidth": "75px"
+        }, {
+          "mDataProp": "start",
+					"sWidth": "85px"
+        }, {
+          "mDataProp": "time",
+					"sWidth": "95px"
+        }, {
+          "mDataProp": "status",
+          "sWidth": "100px"
+        }, {
+          "mDataProp": "state",
+          "bVisible": false
+        }, {
+          "mDataProp": "action",
+					"sWidth": "100px"
+        }
+      ];
+    }
+    return [
+      {
+        "mDataProp": "userid"
+      }, {
+        "mDataProp": "tablename"
+      }, {
+        "mDataProp": "start"
+      }, {
+        "mDataProp": "time"
+      }, {
+        "mDataProp": "status",
+        "sWidth": "150px"
+      }, {
+        "mDataProp": "state",
+        "bVisible": false
+      }, {
+        "mDataProp": "action"
+      }
+    ];
+  };
+  var process_row = function(row, data, rowIdx, dataIdx) {
+    var action_td = $('td:last-child', row);
+    if (action_td.html() === '') {
+      action_td.append("<a href='" + (Routes.blur_query_path(data['id'])) + "' class='more_info' style='margin-right: 3px'>More Info</a>");
+      if (data['state'] === 'Running' && data['can_update']) {
+        action_td.append("<form accept-charset='UTF-8' action='" + (Routes.cancel_blur_query_path(data['id'])) + "' class='cancel' data-remote='true' method='put'><div style='margin:0;padding:0;display:inline'><input name='_method' type='hidden' value='put'></div><input class='cancel_query_button btn btn-small' type='submit' value='Cancel'><img src='/assets/loading.gif' style='display:none'></form>");
+      }
+    }
+    var time = data.time.substring(0, data.time.indexOf(' ')).split(':');
+    var timeModifier = data.time.substring(data.time.indexOf(' ') + 1) === 'PM';
+    var timeInSecs = (timeModifier ? parseInt(time[0], 10) + 12 : parseInt(time[0], 10)) * 3600 + parseInt(time[1], 10) * 60 + parseInt(time[2], 10);
+    var dateNow = new Date();
+    var timeNowSecs = dateNow.getHours() * 3600 + dateNow.getMinutes() * 60 + dateNow.getSeconds();
+    if (data.state === 'Running' && Math.abs(timeNowSecs - timeInSecs) > 60) {
+      $(row).addClass('oldRunning');
+    }
+    return row;
+  };
+  var add_refresh_rates = function(data_table) {
+    var refresh_content = '<div class="span3">Auto Refresh: <div class="btn-group">';
+    var options = [
+      {
+        'key': 'Off',
+        'value': -1
+      }, {
+        'key': '10s',
+        'value': 10
+      }, {
+        'key': '1m',
+        'value': 60
+      }, {
+        'key': '10m',
+        'value': 600
+      }
+    ];
+    $.each(options, function(idx, val) {
+      var link_class = idx === 0 ? 'btn-primary' : '';
+      refresh_content += "<a href='javascript:void(0)' class='refresh_option " + link_class + " btn' data-refresh_val='" + val.value + "'>" + val.key + "</a>";
+    });
+    refresh_content += '</div></div>';
+    $('#queries-table_wrapper > .row:first-child').prepend(refresh_content);
+    $('.dataTables_wrapper .row .span3:first-child .btn-group').append('<a id="refresh-queries" class="btn"><i class="icon-refresh"/></a>');
+    $('#refresh-queries').click(function() {
+        clearTimeout(refresh_timeout);
+        data_table.fnReloadAjax();
+    });
+    $('a.refresh_option').click(function() {
+      $('a.refresh_option').removeClass('btn-primary');
+      $(this).addClass('btn-primary');
+      var prev_refresh_rate = refresh_rate;
+      refresh_rate = $(this).data('refresh_val');
+      if (prev_refresh_rate === -1) {
+        data_table.fnReloadAjax();
+      }
+      else if (refresh_rate === -1 && refresh_timeout)
+      {
+        clearTimeout(refresh_timeout);
+      }
+    });
+  };
+  var truncate = function(value, length, ommission) {
+    if (!value) return null;
+    if (!(value.length > length)) return value;
+    return "" + (value.substring(0, length)) + (ommission != null ? ommission : {
+      ommission: ''
+    });
+  };
+  $('.more_info').live('click', function(e) {
+    var self = $(this);
+    self.after(Spinner.clone());
+    e.preventDefault();
+    $.ajax({
+      url: self.attr('href'),
+      type: 'GET',
+      success: function(data) {
+        self.siblings('#loading-spinner').remove();
+        $().popup({
+          title: "Additional Info",
+          titleClass: 'title',
+          body: data
+        });
+      }
+    });
+  });
+  $('.cancel_query_button').live('click', function() {
+    $(this).siblings('img').show();
+  });
+  load_queries();
+});

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/blur_tables.js
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/blur_tables.js b/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/blur_tables.js
new file mode 100644
index 0000000..22db2f4
--- /dev/null
+++ b/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/blur_tables.js
@@ -0,0 +1,13 @@
+//= require jquery.dynatree
+//= require bootstrap-tooltip
+//= require bootstrap-popover
+//= require sorttable
+//= require_tree .
+
+$(document).ready(function() {
+  // Dynatree Setup
+  $.ui.dynatree.nodedatadefaults["icon"] = false;
+
+  // Create the cluster collection and start the stream
+  new ClusterCollection().stream({interval: 10000, update: true});
+});

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/cluster.js
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/cluster.js b/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/cluster.js
new file mode 100644
index 0000000..9d67205
--- /dev/null
+++ b/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/cluster.js
@@ -0,0 +1,279 @@
+var Cluster = Backbone.Model.extend({
+  defaults: {},
+  initialize: function(){
+    this.view = new ClusterView({model: this, id: 'cluster_' + this.id});
+    this.build_child_tables();
+    this.set_running_query_header_state();
+    this.on('change:blur_tables', function(){
+      this.update_child_tables();
+    });
+    this.on('change:safe_mode', function(){
+      $('li#cluster_tab_' + this.get('id') + ' .safemode-icon').toggle();
+    });
+    this.on('table_has_been_queried', function(){
+      this.set_running_query_header_state();
+    });
+  },
+  build_child_tables: function(){
+    this.set({tables: new TableCollection(this.get('blur_tables'), {cluster: this})}, {silent: true});
+  },
+  update_child_tables: function(){
+    this.get('tables').update(this.get('blur_tables'));
+    this.view.set_table_values();
+  },
+  set_running_query_header_state: function(){
+    var tables_queried = this.get('tables').where({queried_recently: true}).length;
+    if (tables_queried > 0) {
+      $('li#cluster_tab_' + this.get('id') + ' .queries-running-icon').show();
+    } else {
+      $('li#cluster_tab_' + this.get('id') + ' .queries-running-icon').hide();
+    }
+  },
+  enable_tables: function(){
+    var selected_tables = this.get('tables').where({state: 'disabled', checked: true});
+    var table_ids = _.map(selected_tables, function(table){ return table.get('id'); });
+    this.send_action_request(selected_tables, _.bind(function(){
+      $().popup({
+        title: "Enable Tables",
+        titleClass: 'title',
+        body: "Are you sure you want to enable these tables?",
+        btns: {
+          "Enable": {
+            "class": "primary",
+            func: _.bind(function() {
+              $.ajax({
+                type: 'PUT',
+                url: Routes.enable_zookeeper_blur_tables_path(CurrentZookeeper, {format: 'json'}),
+                data: {tables: table_ids}
+              });
+              $().closePopup();
+              _.each(selected_tables, function(table){
+                table.set({table_status: 5});
+              });
+              this.view.set_table_state();
+            }, this)
+          },
+          "Cancel": {
+            func: function() {
+              $().closePopup();
+            }
+          }
+        }
+      });
+    }, this));
+  },
+  disable_tables: function(){
+    var selected_tables = this.get('tables').where({state: 'active', checked: true});
+    var table_ids = _.map(selected_tables, function(table){ return table.get('id'); });
+    this.send_action_request(selected_tables, _.bind(function(){
+      $().popup({
+        title: "Disable Tables",
+        titleClass: 'title',
+        body: "Are you sure you want to disable these tables?",
+        btns: {
+          "Disable": {
+            "class": "primary",
+            func: _.bind(function() {
+              $.ajax({
+                type: 'PUT',
+                url: Routes.disable_zookeeper_blur_tables_path(CurrentZookeeper, {format: 'json'}),
+                data: {tables: table_ids}
+              });
+              $().closePopup();
+              _.each(selected_tables, function(table){
+                table.set({table_status: 3});
+              });
+              this.view.set_table_state();
+            }, this)
+          },
+          "Cancel": {
+            func: function() {
+              $().closePopup();
+            }
+          }
+        }
+      });
+    }, this));
+  },
+  delete_tables: function(){
+    var selected_tables = this.get('tables').where({state: 'disabled', checked: true});
+    var table_ids = _.map(selected_tables, function(table){ return table.get('id'); });
+    this.send_action_request(selected_tables, _.bind(function(){
+      var delete_tables_send = function(delete_index) {
+        $.ajax({
+          type: 'DELETE',
+          url: Routes.zookeeper_blur_tables_path(CurrentZookeeper, {format: 'json'}),
+          data: {
+            tables: table_ids,
+            delete_index: delete_index
+          }
+        });
+        $().closePopup();
+        _.each(selected_tables, function(table){
+          table.set({table_status: 1});
+        });
+        this.view.set_table_state();
+      };
+      $().popup({
+        title: "Delete Tables",
+        titleClass: 'title',
+        body: "Do you want to delete all of the underlying table indicies?",
+        btns: {
+          "Delete tables and indicies": {
+            "class": "danger",
+            func: _.bind(delete_tables_send, this, true)
+          },
+          "Delete tables only": {
+            "class": "warning",
+            func: _.bind(delete_tables_send, this, false)
+          },
+          "Cancel": {
+            func: function() {
+              $().closePopup();
+            }
+          }
+        }
+      });
+    }, this));
+  },
+  send_action_request: function(selected_tables, confirm_function){
+    if (_.find(selected_tables, function(table){
+        return table.get('queried_recently') && table.get('state') == 'active';
+    })){
+      $().popup({
+        title: 'Warning! You are attempting to change an active table!',
+        titleClass: 'title',
+        body: 'You are attempting to perform an action on a recently queried table, Do you wish to continue?',
+        btns: {
+          "Continue": { class: 'danger', func: confirm_function },
+          "Cancel": { func: function() { $().closePopup(); } }
+        }
+      });
+    } else {
+      confirm_function();
+    }
+  },
+  perform_action: function(state, action){
+    var selected_tables = this.get('tables').where({state: state, checked: true});
+    if (_.find(selected_tables, function(table){ return table.get('queried_recently'); })){
+      $().popup({
+        title: 'Warning! You are attempting to ' + action + ' an active table!',
+        titleClass: 'title',
+        body: '<div>You are attempting to perform an action on a recently queried table, Do you wish to continue?</div>',
+        btns: {
+          "Enable": {
+            class: 'danger',
+            func: _.bind(function(){
+              this.move_forward_with_action(action, selected_tables);
+            }, this)
+          },
+          "Cancel": {
+            func: function() {
+              $().closePopup();
+            }
+          }
+        }
+      });
+    } else {
+      this.move_forward_with_action(action, selected_tables);
+    }
+  }
+});
+
+var ClusterCollection = Backbone.StreamCollection.extend({
+  model: Cluster,
+  url: Routes.zookeeper_blur_tables_path(CurrentZookeeper, {format: 'json'}),
+  initialize: function(models, options){
+    this.on('add', function(collection){
+      var container = $('#tables-wrapper');
+      var renderedView = $(collection.view.render().el);
+      container.children('div').length === 0 ? container.html(renderedView.addClass('active')) : container.append(renderedView);
+    });
+  }
+});
+
+var ClusterView = Backbone.View.extend({
+  className: 'tab-pane cluster',
+  template: JST['templates/blur_table/cluster_view'],
+  events: {
+    'click .bulk-action-checkbox' : 'set_table_state',
+    'click .check-all' : 'check_all_boxes',
+    'click .btn[data-action=enable]' : 'enable_tables',
+    'click .btn[data-action=disable]' : 'disable_tables',
+    'click .btn[data-action=delete]' : 'delete_tables'
+  },
+  colspan_lookup : {'active': 6, 'disabled': 4},
+  render: function(){
+    this.$el.html(this.template({cluster: this.model}));
+    this.populate_tables();
+    return this;
+  },
+  populate_tables: function(){
+    var el = $(this.el);
+    this.model.get('tables').each(function(table){
+      elementToAdd = table.view.el.rendered ? table.view.el : table.view.render().el;
+      var table_parent = el.find('.' + table.get('table') + '-table');
+      table_parent.append(elementToAdd);
+      table_parent.siblings('thead').find('.check-all').removeAttr('disabled');
+      sorttable.makeSortable(table_parent.parent()[0]);
+    });
+    this.set_table_values();
+  },
+  set_table_values: function(){
+    var table_prefixes = ['active', 'disabled'];
+    for (var index = 0; index < table_prefixes.length; index++){
+      var table = this.$el.find('.' + table_prefixes[index] + '-table');
+      table.find('.no-data').remove();
+      var table_children_count = table.children().length;
+      this.$el.find('.' + table_prefixes[index] + '-counter').text(table_children_count);
+      if (this.model.get('tables').where({table: table_prefixes[index]}).length <= 0){
+        table.append(this.no_table(this.colspan_lookup[table_prefixes[index]]));
+      }
+    }
+  },
+  set_table_state: function(){
+    var checked_count = this.$el.find('tbody tr.highlighted-row').length;
+    var set_checkbox_state = _.bind(function(){
+      var row_count = this.$el.find('tbody:visible tr:not(.no-data, .changing-state)').length;
+      var check_all = this.$el.find('.tab-pane.active .check-all');
+      if (checked_count === row_count){
+        if (checked_count === 0){
+          check_all.removeAttr('checked');
+          check_all.attr('disabled', 'disabled');
+        } else {
+          check_all.attr('checked', 'checked');
+        }
+      } else {
+        check_all.removeAttr('checked');
+      }
+    }, this);
+    var set_button_state = _.bind(function(){
+      var toggle_button = this.$el.find('.tab-pane.active button');
+      checked_count > 0 ? toggle_button.removeAttr('disabled') : toggle_button.attr('disabled', 'disabled');
+    }, this);
+    set_checkbox_state();
+    set_button_state();
+  },
+  check_all_boxes: function(){
+    var check_all = this.$el.find('.tab-pane.active .check-all');
+    if (check_all.is(':checked')){
+      this.$el.find('.tab-pane.active .bulk-action-checkbox:not(:checked)').click();
+    } else {
+      this.$el.find('.tab-pane.active .bulk-action-checkbox:checked').click();
+    }
+  },
+  no_table: function(colspan){
+    return $('<tr class="no-data"><td/><td colspan="' + colspan + '">No Tables for this Section</td></tr>')
+  },
+  enable_tables: function(event){
+    this.model.enable_tables();
+    this.model.change();
+  },
+  disable_tables: function(event){
+    this.model.disable_tables();
+    this.model.change();
+  },
+  delete_tables: function(event){
+    this.model.delete_tables();
+  }
+});

http://git-wip-us.apache.org/repos/asf/incubator-blur/blob/b0e26648/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/table.js
----------------------------------------------------------------------
diff --git a/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/table.js b/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/table.js
new file mode 100644
index 0000000..3762a55
--- /dev/null
+++ b/contrib/blur-console/blur-admin/app/assets/javascripts/blur_table/table.js
@@ -0,0 +1,189 @@
+//= require flot/flot
+
+var Table = Backbone.Model.extend({
+  defaults: {
+    'checked' : false,
+  },
+  state_lookup : ['deleted', 'deleting', 'disabled', 'disabling', 'active', 'enabling'],
+  table_lookup : ['deleted', 'disabled', 'disabled', 'active', 'active', 'disabled'],
+  colspan_lookup : {'active': 6, 'disabled': 5},
+  initialize: function(){
+    this.view = new TableView({model: this});
+    this.view.render();
+    this.set({
+      state: this.state_lookup[this.get('table_status')],
+      table: this.table_lookup[this.get('table_status')]
+    });
+    this.on('change:table_status', function(){
+      var table = this.get('table')
+      this.set({
+        state: this.state_lookup[this.get('table_status')],
+        table: this.table_lookup[this.get('table_status')],
+        checked: false
+      }, {
+        silent: true
+      });
+      if (this.get('table') !== table){
+        var table_parent = this.collection.cluster.view.$el.find('.' + this.get('table') + '-table');
+        table_parent.append(this.view.el);
+        table_parent.siblings('thead').find('.check-all').removeAttr('disabled');
+        sorttable.makeSortable(table_parent.parent()[0]);
+      }
+    });
+    this.on('change:queried_recently', function(){
+      this.collection.cluster.trigger('table_has_been_queried');
+    });
+    this.on('change', function(){
+      this.view.render();
+    });
+  },
+  parse_uri: function(piece){
+    var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
+    var result = parse_url.exec(this.get('table_uri'));
+    var index = _.indexOf(['url', 'scheme', 'slash', 'host', 'port', 'path', 'query', 'hash'], piece);
+    if (index === -1) throw 'The index, ' + piece + ' does not exist as a part of a uri.';
+    return result[index];
+  },
+  get_terms: function(request_data, success){
+    $.ajax({
+      type: 'GET',
+      url: Routes.terms_blur_table_path(this.get('id'), {format: 'json'}),
+      data: request_data,
+      success: success,
+      error:function (xhr, ajaxOptions, thrownError){
+        alert(thrownError + ": Terms Currently Unavailable");
+      }
+    });
+  },
+  capitalize_first: function(word){
+    return word.charAt(0).toUpperCase() + word.slice(1);
+  }
+});
+
+var TableCollection = Backbone.StreamCollection.extend({
+  model: Table,
+  initialize: function(models, options){
+    this.cluster = options.cluster;
+    this.on('add', function(table){
+      var table_parent = table.collection.cluster.view.$el.find('.' + this.get('table') + '-table');
+      table_parent.append(table.view.el);
+      table_parent.siblings('thead').find('.check-all').removeAttr('disabled');
+      sorttable.makeSortable(table_parent.parent()[0]);
+    });
+    this.on('remove', function(table){
+      table.view.destroy();
+    });
+  }
+});
+
+var TableView = Backbone.View.extend({
+  tagName: 'tr',
+  className: 'blur_table',
+  events: {
+    'click .bulk-action-checkbox' : 'toggle_row',
+    'click .hosts' : 'show_hosts',
+    'click .info' : 'show_schema',
+    'click .comments' : 'show_comments'
+  },
+  template: JST['templates/blur_table/table_row'],
+  render: function(){
+    this.rendered = true;
+    this.$el.removeClass('changing-state')
+    this.$el.attr('blur_table_id', this.model.get('id')).html(this.template({table: this.model})).removeClass('highlighted-row');
+    if (this.model.get('checked')) this.$el.addClass('highlighted-row').find('.bulk-action-checkbox').prop('checked', 'checked');
+    if (['disabling', 'enabling', 'deleting'].indexOf(this.model.get('state')) >= 0) this.$el.addClass('changing-state');
+    return this;
+  },
+  toggle_row: function(){
+    this.model.set({checked: !this.model.get('checked')}, {silent: true});
+    this.$el.toggleClass('highlighted-row');
+  },
+  show_hosts: function(){
+    $.ajax({
+      type: 'GET',
+      url: Routes.hosts_blur_table_path(this.model.get('id'), {format: 'json'}) ,
+      success: _.bind(function(data){
+        var host_modal = $(JST['templates/blur_table/hosts']({table: this.model, hosts: data}));
+        this.setup_filter_tree(host_modal);
+        $().popup({
+          title: 'Additional Host/Shard Info',
+          titleClass: 'title',
+          body: host_modal
+        });
+      }, this)
+    });
+    return false;
+  },
+  show_schema: function(){
+    $.ajax({
+      type: 'GET',
+      url: Routes.schema_blur_table_path(this.model.get('id'), {format: 'json'}) ,
+      success: _.bind(function(data){
+        var schema_modal = $(JST['templates/blur_table/schema']({table: this.model, schema: data}));
+        this.setup_filter_tree(schema_modal.find('.table_info_tree'));
+        $().popup({
+          title: 'Additional Schema Info',
+          titleClass: 'title',
+          body: schema_modal,
+        });
+        var table_model = this.model;
+        schema_modal.on('click', '.terms', function(){
+          var clicked_element = $(this);
+          var request_data =
+          {
+            family: $(this).attr('data-family-name'),
+            column: $(this).attr('data-column-name'),
+            startwith: ' ',
+            size: 20
+          };
+          table_model.get_terms(request_data, _.bind(function(data) {
+            new TermsView({
+              clicked_element: clicked_element,
+              parent: this,
+              terms: data,
+              family: request_data.family,
+              column: request_data.column,
+              table_id: this.get('id')})
+            .render();
+          }, table_model));
+        });
+      }, this)
+    });
+    return false;
+  },
+  show_comments: function(){
+    var comment_modal = $(JST['templates/blur_table/comments']({table: this.model}));
+    $().popup({
+      title: 'Comments',
+      titleClass: 'title',
+      body: comment_modal ,
+      btns: {
+        "Submit": {
+          "class": "primary",
+          func: _.bind(function() {
+            var input_comment = document.getElementById("comments").value;
+            $.ajax({
+              type: 'PUT',
+              url: Routes.comment_blur_table_path(this.model.get('id'), {format: 'json'}) ,
+              data: {comment: input_comment},
+              success: _.bind(function(){
+                this.model.set({comments: input_comment});
+              }, this)
+            });
+
+            $().closePopup();
+          }, this)
+        },
+        "Cancel": {
+          func: function() {
+            $().closePopup();
+          }
+        }
+      }
+    });
+    return false;
+  },
+  setup_filter_tree: function(selector) {
+    return selector.dynatree();
+  }
+});


Mime
View raw message