You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by al...@apache.org on 2015/02/24 17:31:54 UTC
[01/12] ambari git commit: AMBARI-9676. Initial Hive View Submission
to Contrib (alexantonenko)
Repository: ambari
Updated Branches:
refs/heads/branch-2.0.0 828d85ddd -> f5482d89d
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/udfs/UDFServiceTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/udfs/UDFServiceTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/udfs/UDFServiceTest.java
new file mode 100644
index 0000000..9271e97
--- /dev/null
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/udfs/UDFServiceTest.java
@@ -0,0 +1,119 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.udfs;
+
+import org.apache.ambari.view.hive.BaseHiveTest;
+import org.apache.ambari.view.hive.resources.udfs.UDF;
+import org.apache.ambari.view.hive.resources.udfs.UDFService;
+import org.apache.ambari.view.hive.utils.NotFoundFormattedException;
+import org.json.simple.JSONObject;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+
+import static org.easymock.EasyMock.*;
+
+public class UDFServiceTest extends BaseHiveTest {
+ @Rule public ExpectedException thrown = ExpectedException.none();
+ private UDFService udfService;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ udfService = getService(UDFService.class, handler, context);
+ }
+
+ private Response doCreateUDF() {
+ UDFService.UDFRequest request = new UDFService.UDFRequest();
+ request.udf = new UDF();
+ request.udf.setClassname("/tmp/udf.jar");
+ request.udf.setName("TestUDF");
+
+ UriInfo uriInfo = createNiceMock(UriInfo.class);
+ URI uri = UriBuilder.fromUri("http://host/a/b").build();
+ expect(uriInfo.getAbsolutePath()).andReturn(uri);
+
+ HttpServletResponse resp_obj = createNiceMock(HttpServletResponse.class);
+
+ resp_obj.setHeader(eq("Location"), anyString());
+
+ replay(uriInfo, resp_obj);
+ return udfService.create(request, resp_obj, uriInfo);
+ }
+
+ @Test
+ public void createUDF() {
+ Response response = doCreateUDF();
+ Assert.assertEquals(201, response.getStatus());
+
+ JSONObject obj = (JSONObject)response.getEntity();
+ Assert.assertTrue(obj.containsKey("udf"));
+ Assert.assertNotNull(((UDF) obj.get("udf")).getId());
+ Assert.assertFalse(((UDF) obj.get("udf")).getId() == null);
+ }
+
+ @Test
+ public void udfNotFound() {
+ thrown.expect(NotFoundFormattedException.class);
+ udfService.getOne("4242");
+ }
+
+ @Test
+ public void updateUDF() {
+ Response createdUDF = doCreateUDF();
+ Integer createdUdfId = ((UDF) ((JSONObject) createdUDF.getEntity()).get("udf")).getId();
+
+ UDFService.UDFRequest request = new UDFService.UDFRequest();
+ request.udf = new UDF();
+ request.udf.setClassname("/tmp/updatedUDF.jar");
+ request.udf.setName("TestUDF2");
+
+ Response response = udfService.update(request, String.valueOf(createdUdfId));
+ Assert.assertEquals(204, response.getStatus());
+
+ Response response2 = udfService.getOne(String.valueOf(createdUdfId));
+ Assert.assertEquals(200, response2.getStatus());
+
+ JSONObject obj = ((JSONObject) response2.getEntity());
+ Assert.assertTrue(obj.containsKey("udf"));
+ Assert.assertEquals(((UDF) obj.get("udf")).getName(), request.udf.getName());
+ Assert.assertEquals(((UDF) obj.get("udf")).getClassname(), request.udf.getClassname());
+ }
+
+ @Test
+ public void deleteUDF() {
+ Response createdUDF = doCreateUDF();
+ Integer createdUdfId = ((UDF) ((JSONObject) createdUDF.getEntity()).get("udf")).getId();
+
+ Response response = udfService.delete(String.valueOf(createdUdfId));
+ Assert.assertEquals(204, response.getStatus());
+
+ thrown.expect(NotFoundFormattedException.class);
+ udfService.getOne(String.valueOf(createdUdfId));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/utils/HdfsApiMock.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/utils/HdfsApiMock.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/utils/HdfsApiMock.java
new file mode 100644
index 0000000..bb922e2
--- /dev/null
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/utils/HdfsApiMock.java
@@ -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.
+ */
+
+package org.apache.ambari.view.hive.utils;
+
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.FSDataOutputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import static org.easymock.EasyMock.*;
+
+/**
+* Created by roma on 2/6/15.
+*/
+public class HdfsApiMock {
+ private ByteArrayOutputStream fsQueryOutputStream;
+ private ByteArrayOutputStream fsLogsOutputStream;
+ private HdfsApi hdfsApi;
+
+ public HdfsApiMock(String inputFileContent) throws IOException, InterruptedException {
+ setupHdfsApi(inputFileContent);
+ }
+
+ protected void setupHdfsApi(String inputFileContent) throws IOException, InterruptedException {
+ hdfsApi = createNiceMock(HdfsApi.class);
+
+ hdfsApi.copy(anyString(), anyString());
+
+ fsQueryOutputStream = setupQueryOutputStream(hdfsApi);
+ fsLogsOutputStream = setupLogsOutputStream(hdfsApi);
+ setupQueryInputStream(hdfsApi, inputFileContent);
+
+ expect(hdfsApi.mkdir(anyString())).andReturn(true).anyTimes();
+ }
+
+ protected SeekableByteArrayInputStream setupQueryInputStream(HdfsApi hdfsApi, String query) throws IOException, InterruptedException {
+ SeekableByteArrayInputStream inputStreamHQL = new SeekableByteArrayInputStream(query.getBytes());
+ expect(hdfsApi.open(endsWith(".hql"))).andReturn(new FSDataInputStream(inputStreamHQL)).anyTimes();
+ return inputStreamHQL;
+ }
+
+ protected ByteArrayOutputStream setupQueryOutputStream(HdfsApi hdfsApi) throws IOException, InterruptedException {
+ ByteArrayOutputStream queryOutputStream = new ByteArrayOutputStream();
+ FSDataOutputStream fsQueryOutputStream = new FSDataOutputStream(queryOutputStream);
+ expect(hdfsApi.create(endsWith(".hql"), anyBoolean())).andReturn(fsQueryOutputStream);
+ return queryOutputStream;
+ }
+
+ protected ByteArrayOutputStream setupLogsOutputStream(HdfsApi hdfsApi) throws IOException, InterruptedException {
+ ByteArrayOutputStream logsOutputStream = new ByteArrayOutputStream();
+ FSDataOutputStream fsLogsOutputStream = new FSDataOutputStream(logsOutputStream);
+ expect(hdfsApi.create(endsWith("logs"), anyBoolean())).andReturn(fsLogsOutputStream).anyTimes();
+ return logsOutputStream;
+ }
+
+ public HdfsApi getHdfsApi() {
+ return hdfsApi;
+ }
+
+ public ByteArrayOutputStream getQueryOutputStream() {
+ return fsQueryOutputStream;
+ }
+
+ public ByteArrayOutputStream getLogsOutputStream() {
+ return fsLogsOutputStream;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/utils/SeekableByteArrayInputStream.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/utils/SeekableByteArrayInputStream.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/utils/SeekableByteArrayInputStream.java
new file mode 100644
index 0000000..332dc5d
--- /dev/null
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/utils/SeekableByteArrayInputStream.java
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.utils;
+
+import org.apache.hadoop.fs.PositionedReadable;
+import org.apache.hadoop.fs.Seekable;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+/**
+* Created by roma on 2/6/15.
+*/
+public class SeekableByteArrayInputStream extends ByteArrayInputStream
+ implements Seekable, PositionedReadable {
+ public SeekableByteArrayInputStream(byte[] buf) {
+ super(buf);
+ }
+
+ public SeekableByteArrayInputStream(byte buf[], int offset, int length) {
+ super(buf, offset, length);
+ throw new UnsupportedOperationException("Seek code assumes offset is zero");
+ }
+
+ public void seek(long position) {
+ if (position < 0 || position >= buf.length)
+ throw new IllegalArgumentException("pos = " + position + " buf.lenght = " + buf.length);
+ this.pos = (int) position;
+ }
+
+ public long getPos() {
+ return this.pos;
+ }
+
+ @Override
+ public boolean seekToNewSource(long l) throws IOException {
+ throw new UnsupportedOperationException("seekToNewSource is not supported");
+ }
+
+ @Override
+ public int read(long l, byte[] buffer, int offset, int length) throws IOException {
+ this.seek(l);
+ return this.read(buffer, offset, length);
+ }
+
+ @Override
+ public void readFully(long l, byte[] bytes, int i, int i1) throws IOException {
+ throw new UnsupportedOperationException("readFully is not supported");
+ }
+
+ @Override
+ public void readFully(long l, byte[] bytes) throws IOException {
+ throw new UnsupportedOperationException("readFully is not supported");
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/pom.xml
----------------------------------------------------------------------
diff --git a/contrib/views/pom.xml b/contrib/views/pom.xml
index 608a8b0..60c3b71 100644
--- a/contrib/views/pom.xml
+++ b/contrib/views/pom.xml
@@ -38,6 +38,7 @@
<module>pig</module>
<module>slider</module>
<module>capacity-scheduler</module>
+ <module>hive</module>
<module>tez</module>
</modules>
<build>
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 3436450..9fc2c90 100644
--- a/pom.xml
+++ b/pom.xml
@@ -299,6 +299,7 @@
<exclude>contrib/ambari-scom/*/rat.txt</exclude>
<exclude>contrib/ambari-scom/metrics-sink/target/**</exclude>
<exclude>contrib/views/*/target/**</exclude>
+ <exclude>contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/**</exclude>
<exclude>contrib/views/files/src/main/resources/ui/bower_components/**</exclude>
<exclude>contrib/views/files/src/main/resources/ui/node/**</exclude>
<exclude>contrib/views/files/src/main/resources/ui/node_modules/**</exclude>
[11/12] ambari git commit: AMBARI-9676. Initial Hive View Submission
to Contrib (alexantonenko)
Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Row.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Row.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Row.java
new file mode 100644
index 0000000..306530a
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Row.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+import java.util.HashSet;
+
+public class Row {
+ private Object[] row;
+
+ public Row(Object[] row) {
+ this(row, null);
+ }
+
+ public Row(Object[] row, HashSet<Integer> selectedColumns) {
+ if (selectedColumns == null || selectedColumns.size() == 0)
+ this.row = row.clone();
+ else {
+ this.row = new Object[selectedColumns.size()];
+ int rowIndex = 0;
+ for (Integer selectedIndex : selectedColumns) {
+ this.row[rowIndex] = row[selectedIndex];
+ rowIndex ++;
+ }
+ }
+ }
+
+ public Object[] getRow() {
+ return row;
+ }
+
+ public void setRow(Object[] row) {
+ this.row = row;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java
new file mode 100644
index 0000000..fd1ecb5
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Utils.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+import org.apache.hive.service.cli.thrift.TStatus;
+import org.apache.hive.service.cli.thrift.TStatusCode;
+
+public class Utils {
+ static void verifySuccess(TStatus status, String comment) throws HiveClientException {
+ if (status.getStatusCode() != TStatusCode.SUCCESS_STATUS &&
+ status.getStatusCode() != TStatusCode.SUCCESS_WITH_INFO_STATUS) {
+ String message = (status.getErrorMessage() != null) ? status.getErrorMessage() : "";
+ throw new HiveErrorStatusException(status.getStatusCode(), message + ": " + comment);
+ }
+ }
+
+ public static class HiveAuthenticationParams {
+ public static final String AUTH_TYPE = "auth";
+ // We're deprecating this variable's name.
+ public static final String AUTH_QOP_DEPRECATED = "sasl.qop";
+ public static final String AUTH_QOP = "saslQop";
+ public static final String AUTH_SIMPLE = "noSasl";
+ public static final String AUTH_TOKEN = "delegationToken";
+ public static final String AUTH_USER = "user";
+ public static final String AUTH_PRINCIPAL = "principal";
+ public static final String AUTH_PASSWD = "password";
+ public static final String AUTH_KERBEROS_AUTH_TYPE = "kerberosAuthType";
+ public static final String AUTH_KERBEROS_AUTH_TYPE_FROM_SUBJECT = "fromSubject";
+ public static final String ANONYMOUS_USER = "anonymous";
+ public static final String ANONYMOUS_PASSWD = "anonymous";
+ public static final String USE_SSL = "ssl";
+ public static final String SSL_TRUST_STORE = "sslTrustStore";
+ public static final String SSL_TRUST_STORE_PASSWORD = "trustStorePassword";
+ // We're deprecating the name and placement of this in the parsed map (from hive conf vars to
+ // hive session vars).
+ public static final String TRANSPORT_MODE_DEPRECATED = "hive.server2.transport.mode";
+ public static final String TRANSPORT_MODE = "transportMode";
+ // We're deprecating the name and placement of this in the parsed map (from hive conf vars to
+ // hive session vars).
+ public static final String HTTP_PATH_DEPRECATED = "hive.server2.thrift.http.path";
+ public static final String HTTP_PATH = "httpPath";
+ public static final String SERVICE_DISCOVERY_MODE = "serviceDiscoveryMode";
+ // Don't use dynamic service discovery
+ public static final String SERVICE_DISCOVERY_MODE_NONE = "none";
+ // Use ZooKeeper for indirection while using dynamic service discovery
+ public static final String SERVICE_DISCOVERY_MODE_ZOOKEEPER = "zooKeeper";
+ public static final String ZOOKEEPER_NAMESPACE = "zooKeeperNamespace";
+ // Default namespace value on ZooKeeper.
+ // This value is used if the param "zooKeeperNamespace" is not specified in the JDBC Uri.
+ public static final String ZOOKEEPER_DEFAULT_NAMESPACE = "hiveserver2";
+
+ // Non-configurable params:
+ // Currently supports JKS keystore format
+ public static final String SSL_TRUST_STORE_TYPE = "JKS";
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ViewSessionState.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ViewSessionState.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ViewSessionState.java
new file mode 100644
index 0000000..aa6cd47
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ViewSessionState.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.ql.session.SessionState;
+
+public class ViewSessionState extends SessionState {
+ public ViewSessionState(HiveConf conf) {
+ super(conf);
+ }
+
+ public ViewSessionState(HiveConf conf, String userName) {
+ super(conf, userName);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java
new file mode 100644
index 0000000..7a4ff40
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/DataStoreStorage.java
@@ -0,0 +1,186 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence;
+
+import org.apache.ambari.view.PersistenceException;
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.utils.FilteringStrategy;
+import org.apache.ambari.view.hive.persistence.utils.Indexed;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.WebApplicationException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Engine for storing objects to context DataStore storage
+ */
+public class DataStoreStorage implements Storage {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(DataStoreStorage.class);
+
+ protected ViewContext context;
+
+ /**
+ * Constructor
+ * @param context View Context instance
+ */
+ public DataStoreStorage(ViewContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public synchronized void store(Class model, Indexed obj) {
+ try {
+ if (obj.getId() == null) {
+ int id = nextIdForEntity(context, model);
+ obj.setId(id);
+ }
+ context.getDataStore().store(obj);
+ } catch (PersistenceException e) {
+ throw new ServiceFormattedException("Error while saving object to DataStorage", e);
+ }
+ }
+
+ private static synchronized int nextIdForEntity(ViewContext context, Class aClass) {
+ // auto increment id implementation
+ String lastId = context.getInstanceData(aClass.getName());
+ int newId;
+ if (lastId == null) {
+ newId = 1;
+ } else {
+ newId = Integer.parseInt(lastId) + 1;
+ }
+ context.putInstanceData(aClass.getName(), String.valueOf(newId));
+ return newId;
+ }
+
+ @Override
+ public synchronized <T extends Indexed> T load(Class<T> model, Integer id) throws ItemNotFound {
+ LOG.debug(String.format("Loading %s #%d", model.getName(), id));
+ try {
+ T obj = context.getDataStore().find(model, id);
+ if (obj != null) {
+ return obj;
+ } else {
+ throw new ItemNotFound();
+ }
+ } catch (PersistenceException e) {
+ throw new ServiceFormattedException("Error while finding object in DataStorage", e);
+ }
+ }
+
+ @Override
+ public synchronized <T extends Indexed> List<T> loadAll(Class<? extends T> model, FilteringStrategy filter) {
+ LinkedList<T> list = new LinkedList<T>();
+ LOG.debug(String.format("Loading all %s-s", model.getName()));
+ try {
+ for(T item: context.getDataStore().findAll(model, null)) {
+ if ((filter == null) || filter.isConform(item)) {
+ list.add(item);
+ }
+ }
+ } catch (PersistenceException e) {
+ throw new ServiceFormattedException("Error while finding all objects in DataStorage", e);
+ }
+ return list;
+ }
+
+ @Override
+ public <T extends Indexed> List<T> loadWhere(Class<T> model, String where) {
+ LOG.debug(String.format("Loading all %s-s", model.getName()));
+ try {
+ return new ArrayList<T>(context.getDataStore().findAll(model, where));
+ } catch (PersistenceException e) {
+ throw new ServiceFormattedException("Error while finding objects in DataStorage; where = " + where, e);
+ }
+ }
+
+ @Override
+ public synchronized <T extends Indexed> List<T> loadAll(Class<T> model) {
+ return loadAll(model, new OnlyOwnersFilteringStrategy(this.context.getUsername()));
+ }
+
+ @Override
+ public synchronized void delete(Class model, int id) throws ItemNotFound {
+ LOG.debug(String.format("Deleting %s:%d", model.getName(), id));
+ Object obj = load(model, id);
+ try {
+ context.getDataStore().remove(obj);
+ } catch (PersistenceException e) {
+ throw new ServiceFormattedException("Error while removing object from DataStorage", e);
+ }
+ }
+
+ @Override
+ public boolean exists(Class model, Integer id) {
+ try {
+ return context.getDataStore().find(model, id) != null;
+ } catch (PersistenceException e) {
+ throw new ServiceFormattedException("Error while finding object in DataStorage", e);
+ }
+ }
+
+ public static void storageSmokeTest(ViewContext context) {
+ try {
+ SmokeTestEntity entity = new SmokeTestEntity();
+ entity.setData("42");
+ DataStoreStorage storage = new DataStoreStorage(context);
+ storage.store(SmokeTestEntity.class, entity);
+
+ if (entity.getId() == null) throw new ServiceFormattedException("Ambari Views instance data DB doesn't work properly (auto increment id doesn't work)", null);
+ Integer id = entity.getId();
+ SmokeTestEntity entity2 = storage.load(SmokeTestEntity.class, id);
+ boolean status = entity2.getData().compareTo("42") == 0;
+ storage.delete(SmokeTestEntity.class, id);
+ if (!status) throw new ServiceFormattedException("Ambari Views instance data DB doesn't work properly", null);
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ public static class SmokeTestEntity implements Indexed {
+ private Integer id = null;
+ private String data = null;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java
new file mode 100644
index 0000000..932b58e
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/InstanceKeyValueStorage.java
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.utils.ContextConfigurationAdapter;
+import org.apache.ambari.view.hive.persistence.utils.Indexed;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.apache.commons.configuration.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import javax.ws.rs.WebApplicationException;
+import java.util.List;
+
+
+/**
+ * Persistent storage engine for storing java beans to
+ * instance data
+ */
+@Deprecated
+public class InstanceKeyValueStorage extends KeyValueStorage {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(InstanceKeyValueStorage.class);
+
+ private ContextConfigurationAdapter config = null;
+ private int VALUE_LENGTH_LIMIT = 254;
+
+ /**
+ * Constructor.
+ * @param context View Context instance
+ */
+ public InstanceKeyValueStorage(ViewContext context) {
+ super(context);
+ }
+
+ /**
+ * Returns config instance, adapter to Persistence API
+ * @return config instance
+ */
+ @Override
+ protected synchronized Configuration getConfig() {
+ if (config == null) {
+ config = new ContextConfigurationAdapter(context);
+ }
+ return config;
+ }
+
+ /**
+ * Value is limited to 256 symbols, this code splits value into chunks and saves them as <key>#<chunk_id>
+ * @param modelPropName key
+ * @param json value
+ */
+ protected void write(String modelPropName, String json) {
+ int saved = 0;
+ int page = 1;
+ while (saved < json.length()) {
+ int end = Math.min(saved + VALUE_LENGTH_LIMIT, json.length());
+ String substring = json.substring(saved, end);
+ getConfig().setProperty(modelPropName + "#" + page, substring);
+ saved += VALUE_LENGTH_LIMIT;
+ page += 1;
+ LOG.debug("Chunk saved: " + modelPropName + "#" + page + "=" + substring);
+ }
+ getConfig().setProperty(modelPropName, page - 1);
+ LOG.debug("Write finished: " + modelPropName + " pages:" + (page - 1));
+ }
+
+ /**
+ * Read chunked value (keys format <key>#<chunk_id>)
+ * @param modelPropName key
+ * @return value
+ */
+ protected String read(String modelPropName) {
+ StringBuilder result = new StringBuilder();
+ int pages = getConfig().getInt(modelPropName);
+ LOG.debug("Read started: " + modelPropName + " pages:" + pages);
+
+ for(int page = 1; page <= pages; page++) {
+ String substring = getConfig().getString(modelPropName + "#" + page);
+ LOG.debug("Chunk read: " + modelPropName + "#" + page + "=" + substring);
+ if (substring != null) {
+ result.append(substring);
+ }
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * Remove chunked value (keys format <key>#<chunk_id>)
+ * @param modelPropName key
+ */
+ protected void clear(String modelPropName) {
+ int pages = getConfig().getInt(modelPropName);
+ LOG.debug("Clean started: " + modelPropName + " pages:" + pages);
+
+ for(int page = 1; page <= pages; page++) {
+ getConfig().clearProperty(modelPropName + "#" + page);
+ LOG.debug("Chunk clean: " + modelPropName + "#" + page);
+ }
+ getConfig().clearProperty(modelPropName);
+ }
+
+ public static void storageSmokeTest(ViewContext context) {
+ try {
+ final String property = "test.smoke.property";
+ context.putInstanceData(property, "42");
+ boolean status = context.getInstanceData(property).equals("42");
+ context.removeInstanceData(property);
+ if (!status) throw new ServiceFormattedException("Ambari Views instance data DB doesn't work properly", null);
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ @Override
+ public <T extends Indexed> List<T> loadWhere(Class<T> model, String where) {
+ throw new NotImplementedException();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/KeyValueStorage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/KeyValueStorage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/KeyValueStorage.java
new file mode 100644
index 0000000..e8a904c
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/KeyValueStorage.java
@@ -0,0 +1,162 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence;
+
+import com.google.gson.Gson;
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.utils.FilteringStrategy;
+import org.apache.ambari.view.hive.persistence.utils.Indexed;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
+import org.apache.commons.configuration.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Engine for storing objects to key-value storage
+ */
+public abstract class KeyValueStorage implements Storage {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(KeyValueStorage.class);
+ protected final Gson gson = new Gson();
+ protected ViewContext context;
+
+ /**
+ * Constructor
+ * @param context View Context instance
+ */
+ public KeyValueStorage(ViewContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Returns config instance, adapter to Persistence API
+ * @return config instance
+ */
+ protected abstract Configuration getConfig();
+
+ @Override
+ public <T extends Indexed> void store(Class<T> model, Indexed obj) {
+ String modelIndexingPropName = getIndexPropertyName(model);
+
+ if (obj.getId() == null) {
+ int lastIndex = getConfig().getInt(modelIndexingPropName, 0);
+ lastIndex ++;
+ getConfig().setProperty(modelIndexingPropName, lastIndex);
+ obj.setId(lastIndex);
+ }
+
+ String modelPropName = getItemPropertyName(model, obj.getId());
+ String json = serialize(obj);
+ write(modelPropName, json);
+ }
+
+ @Override
+ public <T extends Indexed> T load(Class<T> model, Integer id) throws ItemNotFound {
+ String modelPropName = getItemPropertyName(model, id);
+ LOG.debug(String.format("Loading %s", modelPropName));
+ if (getConfig().containsKey(modelPropName)) {
+ String json = read(modelPropName);
+ LOG.debug(String.format("json: %s", json));
+ return deserialize(model, json);
+ } else {
+ throw new ItemNotFound();
+ }
+ }
+
+ /**
+ * Write json to storage
+ * @param modelPropName key
+ * @param json value
+ */
+ protected void write(String modelPropName, String json) {
+ getConfig().setProperty(modelPropName, json);
+ }
+
+ /**
+ * Read json from storage
+ * @param modelPropName key
+ * @return value
+ */
+ protected String read(String modelPropName) {
+ return getConfig().getString(modelPropName);
+ }
+
+ /**
+ * Remove line from storage
+ * @param modelPropName key
+ */
+ protected void clear(String modelPropName) {
+ getConfig().clearProperty(modelPropName);
+ }
+
+ protected String serialize(Indexed obj) {
+ return gson.toJson(obj);
+ }
+
+ protected <T extends Indexed> T deserialize(Class<T> model, String json) {
+ return gson.fromJson(json, model);
+ }
+
+ @Override
+ public synchronized <T extends Indexed> List<T> loadAll(Class<? extends T> model, FilteringStrategy filter) {
+ ArrayList<T> list = new ArrayList<T>();
+ String modelIndexingPropName = getIndexPropertyName(model);
+ LOG.debug(String.format("Loading all %s-s", model.getName()));
+ int lastIndex = getConfig().getInt(modelIndexingPropName, 0);
+ for(int i=1; i<=lastIndex; i++) {
+ try {
+ T item = load(model, i);
+ if ((filter == null) || filter.isConform(item)) {
+ list.add(item);
+ }
+ } catch (ItemNotFound ignored) {
+ }
+ }
+ return list;
+ }
+
+ @Override
+ public synchronized <T extends Indexed> List<T> loadAll(Class<T> model) {
+ return loadAll(model, new OnlyOwnersFilteringStrategy(this.context.getUsername()));
+ }
+
+ @Override
+ public synchronized void delete(Class model, int id) {
+ LOG.debug(String.format("Deleting %s:%d", model.getName(), id));
+ String modelPropName = getItemPropertyName(model, id);
+ clear(modelPropName);
+ }
+
+ @Override
+ public boolean exists(Class model, Integer id) {
+ return getConfig().containsKey(getItemPropertyName(model, id));
+ }
+
+ private String getIndexPropertyName(Class model) {
+ return String.format("%s:index", model.getName());
+ }
+
+ private String getItemPropertyName(Class model, int id) {
+ return String.format("%s.%d", model.getName(), id);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java
new file mode 100644
index 0000000..674029a
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/LocalKeyValueStorage.java
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.utils.Indexed;
+import org.apache.ambari.view.hive.utils.MisconfigurationFormattedException;
+import org.apache.commons.configuration.ConfigurationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import java.util.List;
+
+
+/**
+ * Persistent storage engine for storing java beans to
+ * properties file
+ * Path to file should be in 'dataworker.storagePath' parameter
+ */
+@Deprecated
+public class LocalKeyValueStorage extends KeyValueStorage {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(LocalKeyValueStorage.class);
+
+ private PersistentConfiguration config = null;
+
+ /**
+ * Constructor
+ * @param context View Context instance
+ */
+ public LocalKeyValueStorage(ViewContext context) {
+ super(context);
+ }
+
+ /**
+ * Returns config instance
+ * @return config instance
+ */
+ @Override
+ protected synchronized PersistentConfiguration getConfig() {
+ if (config == null) {
+ String fileName = context.getProperties().get("dataworker.storagePath");
+ if (fileName == null) {
+ String msg = "dataworker.storagePath is not configured!";
+ LOG.error(msg);
+ throw new MisconfigurationFormattedException("dataworker.storagePath");
+ }
+ try {
+ config = new PersistentConfiguration(fileName);
+ } catch (ConfigurationException e) {
+ e.printStackTrace();
+ }
+ }
+ return config;
+ }
+
+ @Override
+ public <T extends Indexed> List<T> loadWhere(Class<T> model, String where) {
+ throw new NotImplementedException();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/PersistentConfiguration.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/PersistentConfiguration.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/PersistentConfiguration.java
new file mode 100644
index 0000000..b8405ff
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/PersistentConfiguration.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
+
+import java.io.File;
+
+/**
+ * Configuration enables all necessary options for PropertiesConfiguration:
+ * auto-save, auto-reloading, no delimiter parsing and other
+ */
+@Deprecated
+public class PersistentConfiguration extends PropertiesConfiguration {
+ /**
+ * Constructor
+ * @param fileName path to data file
+ * @throws ConfigurationException
+ */
+ public PersistentConfiguration(String fileName) throws ConfigurationException {
+ super();
+
+ File config = new File(fileName);
+ setFile(config);
+ this.setAutoSave(true);
+ this.setReloadingStrategy(new FileChangedReloadingStrategy());
+ this.setDelimiterParsingDisabled(true);
+ this.setListDelimiter((char) 0);
+
+ if (config.exists()) {
+ this.load();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java
new file mode 100644
index 0000000..fb75670
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/Storage.java
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence;
+
+import org.apache.ambari.view.hive.persistence.utils.FilteringStrategy;
+import org.apache.ambari.view.hive.persistence.utils.Indexed;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+
+import java.util.List;
+
+/**
+ * Object storage interface
+ */
+public interface Storage {
+ /**
+ * Persist object to DB. It should be Indexed
+ * @param obj object to save
+ */
+ <T extends Indexed> void store(Class<T> model, Indexed obj);
+
+ /**
+ * Load object
+ * @param model bean class
+ * @param id identifier
+ * @return bean instance
+ * @throws ItemNotFound thrown if item with id was not found in DB
+ */
+ <T extends Indexed> T load(Class<T> model, Integer id) throws ItemNotFound;
+
+ /**
+ * Load all objects of given bean class
+ * @param model bean class
+ * @param filter filtering strategy (return only those objects that conform condition)
+ * @param <T> bean class
+ * @return list of filtered objects
+ */
+ <T extends Indexed> List<T> loadAll(Class<? extends T> model, FilteringStrategy filter);
+
+ /**
+ * Load all objects of given bean class
+ * @param model bean class
+ * @param where filtering strategy (where clause)
+ * @param <T> bean class
+ * @return list of filtered objects
+ */
+ <T extends Indexed> List<T> loadWhere(Class<T> model, String where);
+
+ /**
+ * Load all objects of given bean class
+ * @param model bean class
+ * @param <T> bean class
+ * @return list of all objects
+ */
+ <T extends Indexed> List<T> loadAll(Class<T> model);
+
+ /**
+ * Delete object
+ * @param model bean class
+ * @param id identifier
+ */
+ void delete(Class model, int id) throws ItemNotFound;
+
+ /**
+ * Check is object exists
+ * @param model bean class
+ * @param id identifier
+ * @return true if exists
+ */
+ boolean exists(Class model, Integer id);
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/ContextConfigurationAdapter.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/ContextConfigurationAdapter.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/ContextConfigurationAdapter.java
new file mode 100644
index 0000000..afc4c78
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/ContextConfigurationAdapter.java
@@ -0,0 +1,260 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence.utils;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.commons.configuration.Configuration;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Persistence API to Apache Configuration adapter
+ */
+@Deprecated
+public class ContextConfigurationAdapter implements Configuration {
+ private ViewContext context;
+
+ /**
+ * Constructor of adapter
+ * @param context View Context
+ */
+ public ContextConfigurationAdapter(ViewContext context) {
+ this.context = context;
+ }
+
+ @Override
+ public Configuration subset(String prefix) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return context.getInstanceData().isEmpty();
+ }
+
+ @Override
+ public boolean containsKey(String s) {
+ Map<String, String> data = context.getInstanceData();
+ return data.containsKey(s);
+ }
+
+ @Override
+ public void addProperty(String s, Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setProperty(String s, Object o) {
+ context.putInstanceData(s, o.toString());
+ }
+
+ @Override
+ public void clearProperty(String key) {
+ context.removeInstanceData(key);
+ }
+
+ @Override
+ public void clear() {
+ for (String key : context.getInstanceData().keySet())
+ context.removeInstanceData(key);
+ }
+
+ @Override
+ public Object getProperty(String key) {
+ return context.getInstanceData(key);
+ }
+
+ @Override
+ public Iterator getKeys(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Iterator getKeys() {
+ return context.getInstanceData().keySet().iterator();
+ }
+
+ @Override
+ public Properties getProperties(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean getBoolean(String s) {
+ return getBoolean(s, null);
+ }
+
+ @Override
+ public boolean getBoolean(String s, boolean b) {
+ return getBoolean(s, (Boolean)b);
+ }
+
+ @Override
+ public Boolean getBoolean(String s, Boolean aBoolean) {
+ String data = context.getInstanceData(s);
+ return (data != null)?Boolean.parseBoolean(data):aBoolean;
+ }
+
+ @Override
+ public byte getByte(String s) {
+ return getByte(s, null);
+ }
+
+ @Override
+ public byte getByte(String s, byte b) {
+ return getByte(s, (Byte)b);
+ }
+
+ @Override
+ public Byte getByte(String s, Byte aByte) {
+ String data = context.getInstanceData(s);
+ return (data != null)?Byte.parseByte(data):aByte;
+ }
+
+ @Override
+ public double getDouble(String s) {
+ return getDouble(s, null);
+ }
+
+ @Override
+ public double getDouble(String s, double v) {
+ return getDouble(s, (Double)v);
+ }
+
+ @Override
+ public Double getDouble(String s, Double aDouble) {
+ String data = context.getInstanceData(s);
+ return (data != null)?Double.parseDouble(data):aDouble;
+ }
+
+ @Override
+ public float getFloat(String s) {
+ return getFloat(s, null);
+ }
+
+ @Override
+ public float getFloat(String s, float v) {
+ return getFloat(s, (Float)v);
+ }
+
+ @Override
+ public Float getFloat(String s, Float aFloat) {
+ String data = context.getInstanceData(s);
+ return (data != null)?Float.parseFloat(data):aFloat;
+ }
+
+ @Override
+ public int getInt(String s) {
+ return getInteger(s, null);
+ }
+
+ @Override
+ public int getInt(String s, int i) {
+ return getInteger(s, i);
+ }
+
+ @Override
+ public Integer getInteger(String s, Integer integer) {
+ String data = context.getInstanceData(s);
+ return (data != null)?Integer.parseInt(data):integer;
+ }
+
+ @Override
+ public long getLong(String s) {
+ return getLong(s, null);
+ }
+
+ @Override
+ public long getLong(String s, long l) {
+ return getLong(s, (Long)l);
+ }
+
+ @Override
+ public Long getLong(String s, Long aLong) {
+ String data = context.getInstanceData(s);
+ return (data != null)?Long.parseLong(data):aLong;
+ }
+
+ @Override
+ public short getShort(String s) {
+ return getShort(s, null);
+ }
+
+ @Override
+ public short getShort(String s, short i) {
+ return getShort(s, (Short)i);
+ }
+
+ @Override
+ public Short getShort(String s, Short aShort) {
+ String data = context.getInstanceData(s);
+ return (data != null)?Short.parseShort(data):aShort;
+ }
+
+ @Override
+ public BigDecimal getBigDecimal(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public BigDecimal getBigDecimal(String s, BigDecimal bigDecimal) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public BigInteger getBigInteger(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public BigInteger getBigInteger(String s, BigInteger bigInteger) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String getString(String s) {
+ return context.getInstanceData(s);
+ }
+
+ @Override
+ public String getString(String s, String s2) {
+ String data = getString(s);
+ return (data != null)?data:s2;
+ }
+
+ @Override
+ public String[] getStringArray(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List getList(String s) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public List getList(String s, List list) {
+ throw new UnsupportedOperationException();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/FilteringStrategy.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/FilteringStrategy.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/FilteringStrategy.java
new file mode 100644
index 0000000..79aa2f9
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/FilteringStrategy.java
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence.utils;
+
+/**
+ * Filtering strategy for stored objects
+ */
+public interface FilteringStrategy {
+ /**
+ * Check whether item conforms chosen filter or not
+ * @param item item to check
+ * @return true if item conforms this filter
+ */
+ boolean isConform(Indexed item);
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/Indexed.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/Indexed.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/Indexed.java
new file mode 100644
index 0000000..1d80920
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/Indexed.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence.utils;
+
+/**
+ * Interface to represent item with identifier
+ */
+public interface Indexed {
+ /**
+ * Get the ID
+ * @return ID
+ */
+ Integer getId();
+
+ /**
+ * Set ID
+ * @param id ID
+ */
+ void setId(Integer id);
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/ItemNotFound.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/ItemNotFound.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/ItemNotFound.java
new file mode 100644
index 0000000..3b7e51a
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/ItemNotFound.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence.utils;
+
+/**
+ * Thrown when item was not found in DB
+ */
+public class ItemNotFound extends Exception {
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/OnlyOwnersFilteringStrategy.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/OnlyOwnersFilteringStrategy.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/OnlyOwnersFilteringStrategy.java
new file mode 100644
index 0000000..9f2b4a2
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/OnlyOwnersFilteringStrategy.java
@@ -0,0 +1,33 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence.utils;
+
+public class OnlyOwnersFilteringStrategy implements FilteringStrategy {
+ private final String username;
+
+ public OnlyOwnersFilteringStrategy(String username) {
+ this.username = username;
+ }
+
+ @Override
+ public boolean isConform(Indexed item) {
+ Owned object = (Owned) item;
+ return object.getOwner().compareTo(username) == 0;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/Owned.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/Owned.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/Owned.java
new file mode 100644
index 0000000..460bf37
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/Owned.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence.utils;
+
+/**
+ * Interface to represent item with owner
+ */
+public interface Owned {
+ /**
+ * Get the owner
+ * @return owner
+ */
+ String getOwner();
+
+ /**
+ * Set owner
+ * @param owner owner
+ */
+ void setOwner(String owner);
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/PersonalResource.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/PersonalResource.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/PersonalResource.java
new file mode 100644
index 0000000..1b41edb
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/PersonalResource.java
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence.utils;
+
+public interface PersonalResource extends Indexed, Owned {
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/StorageUtil.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/StorageUtil.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/StorageUtil.java
new file mode 100644
index 0000000..1e67c02
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/persistence/utils/StorageUtil.java
@@ -0,0 +1,94 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.persistence.utils;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.DataStoreStorage;
+import org.apache.ambari.view.hive.persistence.LocalKeyValueStorage;
+import org.apache.ambari.view.hive.persistence.Storage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Storage factory, creates storage of Local or Persistence API type.
+ * Type depends on context configuration: if "dataworker.storagePath" is set,
+ * storage of Local type will be created. Otherwise, Persistence API will be used.
+ *
+ * Storage is singleton.
+ */
+public class StorageUtil {
+ private Storage storageInstance = null;
+
+ protected final static Logger LOG =
+ LoggerFactory.getLogger(StorageUtil.class);
+
+
+ private static Map<String, StorageUtil> viewSingletonObjects = new HashMap<String, StorageUtil>();
+ public static StorageUtil getInstance(ViewContext context) {
+ if (!viewSingletonObjects.containsKey(context.getInstanceName()))
+ viewSingletonObjects.put(context.getInstanceName(), new StorageUtil(context));
+ return viewSingletonObjects.get(context.getInstanceName());
+ }
+
+ public static void dropAllConnections() {
+ viewSingletonObjects.clear();
+ }
+
+ private ViewContext context;
+
+ /**
+ * Constructor of storage util
+ * @param context View Context instance
+ */
+ public StorageUtil(ViewContext context) {
+ this.context = context;
+ }
+
+ /**
+ * Get storage instance. If one is not created, creates instance.
+ * @return storage instance
+ */
+ public synchronized Storage getStorage() {
+ if (storageInstance == null) {
+ String fileName = context.getProperties().get("dataworker.storagePath");
+ if (fileName != null) {
+ LOG.debug("Using local storage in " + fileName + " to store data");
+ // If specifed, use LocalKeyValueStorage - key-value file based storage
+ storageInstance = new LocalKeyValueStorage(context);
+ } else {
+ LOG.debug("Using Persistence API to store data");
+ // If not specifed, use ambari-views Persistence API
+ storageInstance = new DataStoreStorage(context);
+ }
+ }
+ return storageInstance;
+ }
+
+ /**
+ * Set storage to use across all application.
+ * Used in unit tests.
+ * @param storage storage instance
+ */
+ public void setStorage(Storage storage) {
+ storageInstance = storage;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java
new file mode 100644
index 0000000..b8dec17
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/CRUDResourceManager.java
@@ -0,0 +1,133 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.Storage;
+import org.apache.ambari.view.hive.persistence.utils.FilteringStrategy;
+import org.apache.ambari.view.hive.persistence.utils.Indexed;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.persistence.utils.StorageUtil;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+
+import java.util.List;
+
+/**
+ * CRUD resource manager
+ * @param <T> Data type with ID
+ */
+abstract public class CRUDResourceManager<T extends Indexed> {
+ //TODO: refactor: generic parameter gets Fabric for Indexed objects, not objects itself
+ private Storage storage = null;
+
+ protected final Class<? extends T> resourceClass;
+
+ /**
+ * Constructor
+ * @param resourceClass model class
+ */
+ public CRUDResourceManager(Class<? extends T> resourceClass) {
+ this.resourceClass = resourceClass;
+ }
+ // CRUD operations
+
+ /**
+ * Create operation
+ * @param object object
+ * @return model object
+ */
+ protected T create(T object) {
+ object.setId(null);
+ return this.save(object);
+ }
+
+ /**
+ * Read operation
+ * @param id identifier
+ * @return model object
+ * @throws org.apache.ambari.view.hive.persistence.utils.ItemNotFound
+ */
+ protected T read(Integer id) throws ItemNotFound {
+ T object = null;
+ object = getStorage().load(this.resourceClass, id);
+ if (!checkPermissions(object))
+ throw new ItemNotFound();
+ return object;
+ }
+
+ /**
+ * Read all objects
+ * @param filteringStrategy filtering strategy
+ * @return list of filtered objects
+ */
+ protected List<T> readAll(FilteringStrategy filteringStrategy) {
+ return getStorage().loadAll(this.resourceClass, filteringStrategy);
+ }
+
+ /**
+ * Update operation
+ * @param newObject new object
+ * @param id identifier of previous object
+ * @return model object
+ * @throws org.apache.ambari.view.hive.persistence.utils.ItemNotFound
+ */
+ protected T update(T newObject, Integer id) throws ItemNotFound {
+ newObject.setId(id);
+ this.save(newObject);
+ return newObject;
+ }
+
+ /**
+ * Delete operation
+ * @param resourceId object identifier
+ * @throws org.apache.ambari.view.hive.persistence.utils.ItemNotFound
+ */
+ protected void delete(Integer resourceId) throws ItemNotFound {
+ if (!getStorage().exists(this.resourceClass, resourceId)) {
+ throw new ItemNotFound();
+ }
+ getStorage().delete(this.resourceClass, resourceId);
+ }
+
+ // UTILS
+
+ protected T save(T object) {
+ getStorage().store(resourceClass, object);
+ return object;
+ }
+
+ protected Storage getStorage() {
+ if (storage == null) {
+ storage = StorageUtil.getInstance(getContext()).getStorage();
+ }
+ return storage;
+ }
+
+ protected abstract boolean checkPermissions(T object);
+ protected abstract ViewContext getContext();
+
+ protected void cleanupAfterErrorAndThrowAgain(Indexed object, ServiceFormattedException e) {
+ try {
+ delete(object.getId());
+ } catch (ItemNotFound itemNotFound) {
+ throw new ServiceFormattedException("Error in creation, during clean up: " + itemNotFound.toString(), itemNotFound);
+ }
+ throw e;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/PersonalCRUDResourceManager.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/PersonalCRUDResourceManager.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/PersonalCRUDResourceManager.java
new file mode 100644
index 0000000..d1223fb
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/PersonalCRUDResourceManager.java
@@ -0,0 +1,109 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.persistence.utils.PersonalResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Callable;
+
+/**
+ * Resource manager that returns only user owned elements from DB
+ * @param <T> Data type with ID and Owner
+ */
+public class PersonalCRUDResourceManager<T extends PersonalResource> extends CRUDResourceManager<T> {
+ protected ViewContext context;
+ protected boolean ignorePermissions = false;
+
+ private final static Logger LOG =
+ LoggerFactory.getLogger(PersonalCRUDResourceManager.class);
+ /**
+ * Constructor
+ * @param resourceClass model class
+ * @param context View Context instance
+ */
+ public PersonalCRUDResourceManager(Class<? extends T> resourceClass, ViewContext context) {
+ super(resourceClass);
+ this.context = context;
+ }
+
+ @Override
+ public T update(T newObject, Integer id) throws ItemNotFound {
+ T object = getStorage().load(this.resourceClass, id);
+ if (object.getOwner().compareTo(this.context.getUsername()) != 0) {
+ throw new ItemNotFound();
+ }
+
+ newObject.setOwner(this.context.getUsername());
+ return super.update(newObject, id);
+ }
+
+ @Override
+ public T save(T object) {
+ if (!ignorePermissions) {
+ // in threads permissions should be ignored,
+ // because context.getUsername doesn't work. See BUG-27093.
+ object.setOwner(this.context.getUsername());
+ }
+ return super.save(object);
+ }
+
+ @Override
+ protected boolean checkPermissions(T object) {
+ if (ignorePermissions)
+ return true;
+ return object.getOwner().compareTo(this.context.getUsername()) == 0;
+ }
+
+ @Override
+ public ViewContext getContext() {
+ return context;
+ }
+
+ /**
+ * Execute action ignoring objects owner
+ * @param actions callable to execute
+ * @return value returned from actions
+ * @throws Exception
+ */
+ public <T> T ignorePermissions(Callable<T> actions) throws Exception {
+ ignorePermissions = true;
+ T result;
+ try {
+ result = actions.call();
+ } finally {
+ ignorePermissions = false;
+ }
+ return result;
+ }
+
+ protected static String getUsername(ViewContext context) {
+ String userName = context.getProperties().get("dataworker.username");
+ if (userName == null || userName.compareTo("null") == 0 || userName.compareTo("") == 0)
+ userName = context.getUsername();
+ return userName;
+ }
+
+ protected String getUsername() {
+ return getUsername(context);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/SharedCRUDResourceManager.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/SharedCRUDResourceManager.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/SharedCRUDResourceManager.java
new file mode 100644
index 0000000..0d2b297
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/SharedCRUDResourceManager.java
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.utils.Indexed;
+
+/**
+ * Resource manager that doesn't restrict access (Allow all)
+ * @param <T> Data type with ID
+ */
+public class SharedCRUDResourceManager<T extends Indexed> extends CRUDResourceManager<T> {
+ protected ViewContext context;
+
+ /**
+ * Constructor
+ * @param responseClass model class
+ * @param context View Context instance
+ */
+ public SharedCRUDResourceManager(Class<T> responseClass, ViewContext context) {
+ super(responseClass);
+ this.context = context;
+ }
+
+ @Override
+ protected boolean checkPermissions(T object) {
+ return true; //everyone has permission
+ }
+
+ @Override
+ protected ViewContext getContext() {
+ return context;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/browser/HiveBrowserService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/browser/HiveBrowserService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/browser/HiveBrowserService.java
new file mode 100644
index 0000000..e5983b9
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/browser/HiveBrowserService.java
@@ -0,0 +1,263 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.browser;
+
+import com.google.inject.Inject;
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.ViewResourceHandler;
+import org.apache.ambari.view.hive.BaseService;
+import org.apache.ambari.view.hive.client.ColumnDescription;
+import org.apache.ambari.view.hive.client.ConnectionPool;
+import org.apache.ambari.view.hive.client.Cursor;
+import org.apache.ambari.view.hive.resources.jobs.ResultsPaginationController;
+import org.apache.ambari.view.hive.utils.BadRequestFormattedException;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.apache.commons.collections4.map.PassiveExpiringMap;
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * Database access resource
+ */
+public class HiveBrowserService {
+ @Inject
+ ViewResourceHandler handler;
+ @Inject
+ protected ViewContext context;
+
+ protected final static Logger LOG =
+ LoggerFactory.getLogger(HiveBrowserService.class);
+
+ private static final long EXPIRING_TIME = 10*60*1000; // 10 minutes
+ private static Map<String, Cursor> resultsCache;
+ public static Map<String, Cursor> getResultsCache() {
+ if (resultsCache == null) {
+ PassiveExpiringMap<String, Cursor> resultsCacheExpiringMap =
+ new PassiveExpiringMap<String, Cursor>(EXPIRING_TIME);
+ resultsCache = Collections.synchronizedMap(resultsCacheExpiringMap);
+ }
+ return resultsCache;
+ }
+
+ /**
+ * Returns list of databases
+ */
+ @GET
+ @Path("database")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response databases(@QueryParam("like")String like,
+ @QueryParam("first") String fromBeginning,
+ @QueryParam("count") Integer count,
+ @QueryParam("columns") final String requestedColumns) {
+ if (like == null)
+ like = "*";
+ else
+ like = "*" + like + "*";
+ String curl = null;
+ try {
+ JSONObject response = new JSONObject();
+ List<String> tables = ConnectionPool.getConnection(context).ddl().getDBList(like);
+ response.put("databases", tables);
+ return Response.ok(response).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (IllegalArgumentException ex) {
+ throw new BadRequestFormattedException(ex.getMessage(), ex);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex, curl);
+ }
+ }
+
+ /**
+ * Returns list of databases
+ */
+ @GET
+ @Path("database.page")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response databasesPaginated(@QueryParam("like")String like,
+ @QueryParam("first") String fromBeginning,
+ @QueryParam("count") Integer count,
+ @QueryParam("searchId") String searchId,
+ @QueryParam("columns") final String requestedColumns) {
+ if (like == null)
+ like = "*";
+ else
+ like = "*" + like + "*";
+ String curl = null;
+ try {
+ final String finalLike = like;
+ return ResultsPaginationController.getInstance(context)
+ .request("databases", searchId, false, fromBeginning, count,
+ new Callable<Cursor>() {
+ @Override
+ public Cursor call() throws Exception {
+ return ConnectionPool.getConnection(context).ddl().getDBListCursor(finalLike);
+ }
+ }).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (IllegalArgumentException ex) {
+ throw new BadRequestFormattedException(ex.getMessage(), ex);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex, curl);
+ }
+ }
+
+ /**
+ * Returns list of databases
+ */
+ @GET
+ @Path("database/{db}/table")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response tablesInDatabase(@PathParam("db") String db,
+ @QueryParam("like")String like,
+ @QueryParam("first") String fromBeginning,
+ @QueryParam("count") Integer count,
+ @QueryParam("columns") final String requestedColumns) {
+ if (like == null)
+ like = "*";
+ else
+ like = "*" + like + "*";
+ String curl = null;
+ try {
+ JSONObject response = new JSONObject();
+ List<String> tables = ConnectionPool.getConnection(context).ddl().getTableList(db, like);
+ response.put("tables", tables);
+ response.put("database", db);
+ return Response.ok(response).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (IllegalArgumentException ex) {
+ throw new BadRequestFormattedException(ex.getMessage(), ex);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex, curl);
+ }
+ }
+
+ /**
+ * Returns list of databases
+ */
+ @GET
+ @Path("database/{db}/table.page")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response tablesInDatabasePaginated(@PathParam("db") final String db,
+ @QueryParam("like")String like,
+ @QueryParam("first") String fromBeginning,
+ @QueryParam("count") Integer count,
+ @QueryParam("searchId") String searchId,
+ @QueryParam("columns") final String requestedColumns) {
+ if (like == null)
+ like = "*";
+ else
+ like = "*" + like + "*";
+ String curl = null;
+ try {
+ final String finalLike = like;
+ return ResultsPaginationController.getInstance(context)
+ .request(db + ":tables", searchId, false, fromBeginning, count,
+ new Callable<Cursor>() {
+ @Override
+ public Cursor call() throws Exception {
+ Cursor cursor = ConnectionPool.getConnection(context).ddl().getTableListCursor(db, finalLike);
+ cursor.selectColumns(requestedColumns);
+ return cursor;
+ }
+ }).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (IllegalArgumentException ex) {
+ throw new BadRequestFormattedException(ex.getMessage(), ex);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex, curl);
+ }
+ }
+
+ /**
+ * Returns list of databases
+ */
+ @GET
+ @Path("database/{db}/table/{table}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response describeTable(@PathParam("db") String db,
+ @PathParam("table") String table,
+ @QueryParam("like") String like,
+ @QueryParam("columns") String requestedColumns,
+ @QueryParam("extended") String extended) {
+ boolean extendedTableDescription = (extended != null && extended.equals("true"));
+ String curl = null;
+ try {
+ JSONObject response = new JSONObject();
+ List<ColumnDescription> columnDescriptions = ConnectionPool.getConnection(context).ddl()
+ .getTableDescription(db, table, like, extendedTableDescription);
+ response.put("columns", columnDescriptions);
+ response.put("database", db);
+ response.put("table", table);
+ return Response.ok(response).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (IllegalArgumentException ex) {
+ throw new BadRequestFormattedException(ex.getMessage(), ex);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex, curl);
+ }
+ }
+
+ /**
+ * Returns list of databases
+ */
+ @GET
+ @Path("database/{db}/table/{table}.page")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response describeTablePaginated(@PathParam("db") final String db,
+ @PathParam("table") final String table,
+ @QueryParam("like") final String like,
+ @QueryParam("first") String fromBeginning,
+ @QueryParam("searchId") String searchId,
+ @QueryParam("count") Integer count,
+ @QueryParam("columns") final String requestedColumns) {
+ String curl = null;
+ try {
+ return ResultsPaginationController.getInstance(context)
+ .request(db + ":tables:" + table + ":columns", searchId, false, fromBeginning, count,
+ new Callable<Cursor>() {
+ @Override
+ public Cursor call() throws Exception {
+ Cursor cursor = ConnectionPool.getConnection(context).ddl().getTableDescriptionCursor(db, table, like);
+ cursor.selectColumns(requestedColumns);
+ return cursor;
+ }
+ }).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (IllegalArgumentException ex) {
+ throw new BadRequestFormattedException(ex.getMessage(), ex);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex, curl);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileResource.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileResource.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileResource.java
new file mode 100644
index 0000000..633e859
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileResource.java
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.files;
+
+/**
+ * File bean
+ */
+public class FileResource {
+ private String filePath;
+ private String fileContent;
+ private boolean hasNext;
+ private long page;
+ private long pageCount;
+
+ public String getFilePath() {
+ return filePath;
+ }
+
+ public void setFilePath(String filePath) {
+ this.filePath = filePath;
+ }
+
+ public String getFileContent() {
+ return fileContent;
+ }
+
+ public void setFileContent(String fileContent) {
+ this.fileContent = fileContent;
+ }
+
+ public boolean isHasNext() {
+ return hasNext;
+ }
+
+ public void setHasNext(boolean hasNext) {
+ this.hasNext = hasNext;
+ }
+
+ public long getPage() {
+ return page;
+ }
+
+ public void setPage(long page) {
+ this.page = page;
+ }
+
+ public long getPageCount() {
+ return pageCount;
+ }
+
+ public void setPageCount(long pageCount) {
+ this.pageCount = pageCount;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java
new file mode 100644
index 0000000..8d886d5
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/files/FileService.java
@@ -0,0 +1,185 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.files;
+
+import com.google.inject.Inject;
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.ViewResourceHandler;
+import org.apache.ambari.view.hive.BaseService;
+import org.apache.ambari.view.hive.utils.*;
+import org.apache.hadoop.fs.FSDataOutputStream;
+import org.apache.hadoop.fs.FileAlreadyExistsException;
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.*;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * File access resource
+ * API:
+ * GET /:path
+ * read entire file
+ * POST /
+ * create new file
+ * Required: filePath
+ * file should not already exists
+ * PUT /:path
+ * update file content
+ */
+public class FileService extends BaseService {
+ @Inject
+ ViewResourceHandler handler;
+
+ protected final static Logger LOG =
+ LoggerFactory.getLogger(FileService.class);
+
+ /**
+ * Get single item
+ */
+ @GET
+ @Path("{filePath:.*}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getFilePage(@PathParam("filePath") String filePath, @QueryParam("page") Long page) throws IOException, InterruptedException {
+ LOG.debug("Reading file " + filePath);
+ try {
+ FilePaginator paginator = new FilePaginator(filePath, context);
+
+ if (page == null)
+ page = 0L;
+
+ FileResource file = new FileResource();
+ file.setFilePath(filePath);
+ file.setFileContent(paginator.readPage(page));
+ file.setHasNext(paginator.pageCount() > page + 1);
+ file.setPage(page);
+ file.setPageCount(paginator.pageCount());
+
+ JSONObject object = new JSONObject();
+ object.put("file", file);
+ return Response.ok(object).status(200).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (FileNotFoundException ex) {
+ throw new NotFoundFormattedException(ex.getMessage(), ex);
+ } catch (IllegalArgumentException ex) {
+ throw new BadRequestFormattedException(ex.getMessage(), ex);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Delete single item
+ */
+ @DELETE
+ @Path("{filePath:.*}")
+ public Response deleteFile(@PathParam("filePath") String filePath) throws IOException, InterruptedException {
+ try {
+ LOG.debug("Deleting file " + filePath);
+ if (getHdfsApi().delete(filePath, false)) {
+ return Response.status(204).build();
+ }
+ throw new NotFoundFormattedException("FileSystem.delete returned false", null);
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Update item
+ */
+ @PUT
+ @Path("{filePath:.*}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response updateFile(FileResourceRequest request,
+ @PathParam("filePath") String filePath) throws IOException, InterruptedException {
+ try {
+ LOG.debug("Rewriting file " + filePath);
+ FSDataOutputStream output = getHdfsApi().create(filePath, true);
+ output.writeBytes(request.file.getFileContent());
+ output.close();
+ return Response.status(204).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Create script
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response createFile(FileResourceRequest request,
+ @Context HttpServletResponse response, @Context UriInfo ui)
+ throws IOException, InterruptedException {
+ try {
+ LOG.debug("Creating file " + request.file.getFilePath());
+ try {
+ FSDataOutputStream output = getHdfsApi().create(request.file.getFilePath(), false);
+ if (request.file.getFileContent() != null) {
+ output.writeBytes(request.file.getFileContent());
+ }
+ output.close();
+ } catch (FileAlreadyExistsException ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex, 400);
+ }
+ response.setHeader("Location",
+ String.format("%s/%s", ui.getAbsolutePath().toString(), request.file.getFilePath()));
+ return Response.status(204).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Checks connection to HDFS
+ * @param context View Context
+ */
+ public static void hdfsSmokeTest(ViewContext context) {
+ try {
+ HdfsApi api = HdfsApi.connectToHDFSApi(context);
+ api.getStatus();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Wrapper object for json mapping
+ */
+ public static class FileResourceRequest {
+ public FileResource file;
+ }
+}
[07/12] ambari git commit: AMBARI-9676. Initial Hive View Submission
to Contrib (alexantonenko)
Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js
new file mode 100644
index 0000000..dc75280
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/explain.js
@@ -0,0 +1,119 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+import utils from 'hive/utils/functions';
+
+export default Ember.ObjectController.extend({
+ cachedExplains: [],
+
+ clearCachedExplainSet: function (jobId) {
+ var existingJob = this.get('cachedExplains').findBy('id', jobId);
+
+ if (existingJob) {
+ this.set('cachedExplains', this.get('cachedExplains').without(existingJob));
+ }
+ },
+
+ initExplain: function () {
+ var cachedExplain;
+
+ cachedExplain = this.get('cachedExplains').findBy('id', this.get('content.id'));
+
+ if (cachedExplain) {
+ this.formatExplainResults(cachedExplain);
+ } else {
+ this.getExplain();
+ }
+ }.observes('content'),
+
+ getExplain: function () {
+ var self = this;
+ var url = this.container.lookup('adapter:application').buildURL();
+ url += '/' + constants.namingConventions.jobs + '/' + this.get('content.id') + '/results?first=true';
+
+ Ember.$.getJSON(url).then(function (data) {
+ var explainSet = self.get('cachedExplains').pushObject(Ember.Object.create({
+ id: self.get('content.id'),
+ explain: data
+ }));
+
+ self.set('content.explain', explainSet);
+
+ self.formatExplainResults(explainSet);
+ });
+ },
+
+ formatExplainResults: function (explainSet) {
+ var formatted = [],
+ orderedNodes = [],
+ currentNode,
+ currentNodeWhitespace,
+ previousNode,
+ formattedExplain = [],
+ getLeadingWhitespacesCount = function (str) {
+ return str.replace(utils.regexes.whitespaces, '$1').length;
+ };
+
+ explainSet = explainSet
+ .get('explain.rows')
+ .map(function (row) {
+ return row[0];
+ })
+ .filter(Boolean)
+ .map(function (str) {
+ return {
+ text: str,
+ parentNode: null,
+ contents: []
+ }
+ });
+
+ for (var i = 0; i < explainSet.length; i++) {
+ currentNode = explainSet[i];
+ previousNode = explainSet[i-1];
+
+ if (i > 0) {
+ currentNodeWhitespace = getLeadingWhitespacesCount(currentNode.text);
+
+ if (currentNodeWhitespace > getLeadingWhitespacesCount(previousNode.text)) {
+ currentNode.parentNode = previousNode;
+ previousNode.contents.pushObject(currentNode);
+ } else {
+ for (var j = i - 1; j >= 0; j--) {
+ if (currentNodeWhitespace === getLeadingWhitespacesCount(explainSet[j].text)) {
+ if (currentNodeWhitespace > 0) {
+ currentNode.parentNode = explainSet[j].parentNode;
+ currentNode.parentNode.contents.pushObject(currentNode);
+ } else {
+ formatted.pushObject(currentNode);
+ }
+
+ break;
+ }
+ }
+ }
+ } else {
+ formatted.pushObject(currentNode);
+ }
+ }
+
+ this.set('formattedExplain', formatted);
+ }
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js
new file mode 100644
index 0000000..f633dd4
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/logs.js
@@ -0,0 +1,95 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.ObjectController.extend({
+ needs: [ constants.namingConventions.loadedFiles ],
+
+ files: Ember.computed.alias('controllers.' + constants.namingConventions.loadedFiles),
+
+ reloadJobLogs: function (job) {
+ var self = this,
+ defer = Ember.RSVP.defer(),
+ handleError = function (err) {
+ self.send('addAlert', constants.namingConventions.alerts.error, err.responseText);
+ defer.reject();
+ };
+
+ job.reload().then(function () {
+ self.get('files').reload(job.get('logFile')).then(function (file) {
+ var fileContent = file.get('fileContent');
+
+ if (fileContent) {
+ job.set('log', fileContent);
+ }
+
+ defer.resolve();
+ },function (err) {
+ handleError(err);
+ });
+ }, function (err) {
+ handleError(err);
+ });
+
+ return defer.promise;
+ },
+
+ listenForUpdates: function (job) {
+ Ember.run.later(this, function () {
+ var self = this;
+
+ this.reloadJobLogs(job).then(function () {
+ var stillRunning = self.isJobRunning(job);
+
+ //if the current model is the same with the one displayed, continue reloading job
+ if (stillRunning && job.get('id') === self.get('content.id')) {
+ self.listenForUpdates(job);
+ } else if (!stillRunning) {
+ job.set('isRunning', undefined);
+
+ if (job.get('id') === self.get('content.id')) {
+ self.transitionToRoute(constants.namingConventions.subroutes.jobResults);
+ }
+ }
+ });
+ }, 2000);
+ },
+
+ isJobRunning: function (job) {
+ var status = job.get('status');
+
+ return status !== constants.statuses.finished &&
+ status !== constants.statuses.canceled &&
+ status !== constants.statuses.closed &&
+ status !== constants.statuses.error;
+ },
+
+ getLogs: function () {
+ var self = this,
+ job = this.get('content');
+
+ if (this.isJobRunning(job)) {
+ job.set('isRunning', true);
+ this.reloadJobLogs(job).then(function () {
+ self.listenForUpdates(job);
+ });
+ }
+ }.observes('content')
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js
new file mode 100644
index 0000000..34e69bd
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index/history-query/results.js
@@ -0,0 +1,134 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.ObjectController.extend({
+ cachedResults: [],
+
+ keepAlive: function (job) {
+ Ember.run.later(this, function () {
+ var self = this;
+ var url = this.container.lookup('adapter:application').buildURL();
+ url += '/' + constants.namingConventions.jobs + '/' + job.get('id') + '/results/keepAlive';
+
+ var existingJob = self.cachedResults.findBy('id', job.get('id'));
+
+ if (existingJob) {
+ Ember.$.getJSON(url).fail(function (data) {
+ //backend issue, this will be split in done and fail callbacks once its fixed.
+ if (data.status === 404) {
+ existingJob.set('results', []);
+ self.send('getNextPage', true, job);
+ } else if (data.status === 200) {
+ self.keepAlive(job);
+ }
+ });
+ }
+ }, 1000 * 300);
+ },
+
+ clearCachedResultsSet: function (jobId) {
+ this.set('cachedResults', this.get('cachedResults').without(this.get('cachedResults').findBy('id', jobId)));
+ },
+
+ initResults: function () {
+ var existingJob;
+
+ if (this.get('content.status') !== constants.statuses.finished) {
+ return;
+ }
+
+ existingJob = this.cachedResults.findBy('id', this.get('content.id'));
+
+ if (existingJob) {
+ this.set('results', existingJob.results.findBy('offset', existingJob.get('offset')));
+ } else {
+ this.send('getNextPage', true);
+ }
+ }.observes('content.status'),
+
+ disableNext: function () {
+ return !this.get('results.hasNext');
+ }.property('results'),
+
+ disablePrevious: function () {
+ return this.cachedResults.findBy('id', this.get('content.id')).results.indexOf(this.get('results')) <= 0;
+ }.property('results'),
+
+ actions: {
+ getNextPage: function (firstPage, job) {
+ var self = this;
+ var id = job ? job.get('id') : this.get('content.id');
+ var existingJob = this.cachedResults.findBy('id', id);
+ var resultsIndex;
+ var url = this.container.lookup('adapter:application').buildURL();
+ url += '/' + constants.namingConventions.jobs + '/' + id + '/results';
+
+ if (firstPage) {
+ url += '?first=true';
+ }
+
+ if (existingJob) {
+ resultsIndex = existingJob.results.indexOf(this.get('results'));
+
+ if (~resultsIndex && resultsIndex < existingJob.get('results.length') - 1) {
+ this.set('results', existingJob.results.objectAt(resultsIndex + 1));
+ return;
+ }
+ }
+
+ Ember.$.getJSON(url).then(function (results) {
+ if (existingJob) {
+ existingJob.results.pushObject(results);
+ existingJob.set('offset', results.offset);
+ } else {
+ self.cachedResults.pushObject(Ember.Object.create({
+ id: id,
+ results: [ results ],
+ offset: results.offset
+ }));
+ }
+
+ //only set results if the method was called for the current model, not after a keepAlive request.
+ if (!job) {
+ self.set('results', results);
+ }
+
+ if (firstPage) {
+ self.keepAlive(job || self.get('content'));
+ }
+ }, function (err) {
+ self.set('error', err.responseText);
+ });
+ },
+
+ getPreviousPage: function () {
+ var existingJob,
+ resultsIndex;
+
+ existingJob = this.cachedResults.findBy('id', this.get('content.id'));
+ resultsIndex = existingJob.results.indexOf(this.get('results'));
+
+ if (resultsIndex > 0) {
+ this.set('results', existingJob.results.objectAt(resultsIndex - 1));
+ }
+ }
+ }
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/insert-udfs.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/insert-udfs.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/insert-udfs.js
new file mode 100644
index 0000000..fcfd113
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/insert-udfs.js
@@ -0,0 +1,58 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.ArrayController.extend({
+ needs: [ constants.namingConventions.udfs, constants.namingConventions.fileResources ],
+
+ model: Ember.A(),
+
+ udfs: Ember.computed.alias('controllers.' + constants.namingConventions.udfs + '.udfs'),
+
+ updateUdfs: function () {
+ var self = this,
+ udfs = this.get('udfs'),
+ udfsWithoutFiles;
+
+ this.clear();
+
+ if (udfs && udfs.get('length')) {
+ udfs.getEach('fileResource.id').uniq().forEach(function (fileResourceId) {
+ if (fileResourceId) {
+ self.pushObject(Ember.Object.create({
+ file: udfs.findBy('fileResource.id', fileResourceId).get('fileResource'),
+ udfs: udfs.filterBy('fileResource.id', fileResourceId)
+ }));
+ }
+ });
+
+ udfsWithoutFiles = udfs.filter(function (udf) {
+ return !udf.get('isNew') && !udf.get('fileResource.id');
+ });
+
+ if (udfsWithoutFiles.get('length')) {
+ self.pushObject(Ember.Object.create({
+ name: "placeholders.select.noFileResource",
+ udfs: udfsWithoutFiles
+ }));
+ }
+ }
+ }.on('init').observes('udfs.@each.isNew')
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/job.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/job.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/job.js
new file mode 100644
index 0000000..5bb1cd5
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/job.js
@@ -0,0 +1,49 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.ObjectController.extend({
+ needs: [ constants.namingConventions.history, constants.namingConventions.loadedFiles ],
+
+ files: Ember.computed.alias('controllers.' + constants.namingConventions.loadedFiles),
+
+ canStop: function () {
+ return this.get('status') === constants.statuses.running ||
+ this.get('status') === constants.statuses.initialized ||
+ this.get('status') === constants.statuses.pending;
+ }.property('status'),
+
+ actions: {
+ loadFile: function () {
+ var self = this;
+ if (!this.get('file')) {
+ this.get('files').loadFile(this.get('model.queryFile')).then(function (file) {
+ self.set('file', file);
+ });
+ }
+
+ this.set('expanded', !this.get('expanded'));
+ },
+
+ stop: function () {
+ this.send('interruptJob', this.get('model'));
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/loaded-files.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/loaded-files.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/loaded-files.js
new file mode 100644
index 0000000..3a11171
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/loaded-files.js
@@ -0,0 +1,65 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.ArrayController.extend({
+ contains: function (obj) {
+ if (typeof obj === 'string') {
+ return this.findBy('id', obj);
+ }
+
+ return this._super(obj);
+ },
+
+ loadFile: function (path) {
+ var self = this;
+ var defer = Ember.RSVP.defer();
+ var file = this.contains(path);
+
+ if (file) {
+ defer.resolve(file);
+ } else {
+ this.store.find(constants.namingConventions.file, path).then(function (file) {
+ self.pushObject(file);
+ defer.resolve(file);
+ }, function (err) {
+ defer.reject(err);
+ });
+ }
+
+ return defer.promise;
+ },
+
+ reload: function (path) {
+ var defer = Ember.RSVP.defer();
+
+ this.store.find(constants.namingConventions.file, path).then(function (file) {
+ file.reload().then(function (reloadedFile) {
+ defer.resolve(reloadedFile);
+ }, function (err) {
+ defer.reject(err);
+ });
+ }, function (err) {
+ defer.reject(err);
+ });
+
+ return defer.promise;
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-delete.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-delete.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-delete.js
new file mode 100644
index 0000000..0fbcd6b
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-delete.js
@@ -0,0 +1,33 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Controller.extend({
+ actions: {
+ delete: function () {
+ this.send('closeModal');
+ this.defer.resolve();
+ },
+
+ close: function () {
+ this.send('closeModal');
+ this.defer.reject();
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-save.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-save.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-save.js
new file mode 100644
index 0000000..6dc1349
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/modal-save.js
@@ -0,0 +1,33 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Controller.extend({
+ actions: {
+ save: function () {
+ this.send('closeModal');
+ this.defer.resolve(this.get('text'));
+ },
+
+ close: function () {
+ this.send('closeModal');
+ this.defer.reject();
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js
new file mode 100644
index 0000000..e97c0e6
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/open-queries.js
@@ -0,0 +1,320 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.ArrayController.extend({
+ needs: [ constants.namingConventions.databases,
+ constants.namingConventions.loadedFiles,
+ constants.namingConventions.jobResults,
+ constants.namingConventions.jobExplain,
+ constants.namingConventions.columns,
+ constants.namingConventions.index,
+ constants.namingConventions.settings
+ ],
+
+ databases: Ember.computed.alias('controllers.' + constants.namingConventions.databases),
+
+ files: Ember.computed.alias('controllers.' + constants.namingConventions.loadedFiles),
+
+ jobResults: Ember.computed.alias('controllers.' + constants.namingConventions.jobResults),
+
+ jobExplain: Ember.computed.alias('controllers.' + constants.namingConventions.jobExplain),
+
+ index: Ember.computed.alias('controllers.' + constants.namingConventions.index),
+
+ settings: Ember.computed.alias('controllers.' + constants.namingConventions.settings),
+
+ init: function () {
+ this._super();
+
+ this.set('queryTabs', Ember.ArrayProxy.create({ content: Ember.A([])}));
+ },
+
+ pushObject: function (queryFile, model) {
+ return this._super(queryFile || Ember.Object.create({
+ id: model.get('id'),
+ fileContent: ""
+ }));
+ },
+
+ getTabForModel: function (model) {
+ return this.get('queryTabs').find(function (tab) {
+ return tab.id === model.get('id') && tab.type === model.get('constructor.typeKey');
+ });
+ },
+
+ updateTabSubroute: function (model, path) {
+ var tab = this.get('queryTabs').find(function (tab) {
+ return tab.id === model.get('id') && tab.type === model.get('constructor.typeKey');
+ });
+
+ if (tab) {
+ tab.set('subroute', path);
+ }
+ },
+
+ getQueryForModel: function (model) {
+ return this.find(function (openQuery) {
+ if (model.get('isNew')) {
+ return openQuery.get('id') === model.get('id');
+ }
+
+ return openQuery.get('id') === model.get('queryFile');
+ });
+ },
+
+ update: function (model) {
+ var path,
+ type,
+ currentQuery,
+ defer = Ember.RSVP.defer(),
+ existentTab,
+ self = this,
+ updateSubroute = function () {
+ var isExplainedQuery,
+ subroute;
+
+ if (model.get('constructor.typeKey') === constants.namingConventions.job) {
+ isExplainedQuery = self.get('currentQuery.fileContent').indexOf(constants.namingConventions.explainPrefix) > -1;
+
+ if (isExplainedQuery) {
+ subroute = constants.namingConventions.subroutes.jobExplain;
+ } else {
+ subroute = constants.namingConventions.subroutes.jobLogs;
+ }
+
+ if (!existentTab.get('subroute')) {
+ self.updateTabSubroute(model, subroute);
+ }
+ }
+
+ defer.resolve(isExplainedQuery);
+ };
+
+ existentTab = this.getTabForModel(model);
+
+ if (!existentTab) {
+ type = model.get('constructor.typeKey');
+ path = type === constants.namingConventions.job ?
+ constants.namingConventions.subroutes.historyQuery :
+ constants.namingConventions.subroutes.savedQuery;
+
+ existentTab = this.get('queryTabs').pushObject(Ember.Object.create({
+ name: model.get('title'),
+ id: model.get('id'),
+ visible: true,
+ path: path,
+ type: type
+ }));
+
+ if (model.get('isNew')) {
+ this.set('currentQuery', this.pushObject(null, model));
+
+ defer.resolve();
+ } else {
+ this.get('files').loadFile(model.get('queryFile')).then(function (file) {
+ self.set('currentQuery', self.pushObject(file));
+
+ updateSubroute();
+ });
+
+ if (model.get('logFile') && !model.get('log')) {
+ this.get('files').loadFile(model.get('logFile')).then(function (file) {
+ model.set('log', file.get('fileContent'));
+ });
+ }
+ }
+ } else {
+ currentQuery = this.getQueryForModel(model);
+ this.set('currentQuery', currentQuery);
+
+ updateSubroute();
+ }
+
+ return defer.promise;
+ },
+
+ save: function (model, query) {
+ var tab = this.getTabForModel(model),
+ self = this,
+ wasNew,
+ defer = Ember.RSVP.defer(),
+ jobModel = model,
+ originalId = model.get('id');
+
+ if (!query) {
+ query = this.getQueryForModel(model);
+ }
+
+ if (model.get('isNew')) {
+ wasNew = true;
+ model.set('id', null);
+ }
+
+ //if current query it's a job, convert it to a savedQuery before saving
+ if (model.get('constructor.typeKey') === constants.namingConventions.job) {
+ model = this.store.createRecord(constants.namingConventions.savedQuery, {
+ dataBase: this.get('databases.selectedDatabase.name'),
+ title: model.get('title'),
+ queryFile: model.get('queryFile'),
+ owner: model.get('owner')
+ });
+ }
+
+ model.save().then(function (updatedModel) {
+ tab.set('name', updatedModel.get('title'));
+ jobModel.set('queryId', updatedModel.get('id'));
+
+ var content = self.get('index').prependQuerySettings(query.get('fileContent'));
+ //update query tab path with saved model id if its a new record
+ if (wasNew) {
+ self.get('settings').updateSettingsId(originalId, updatedModel.get('id'));
+ tab.set('id', updatedModel.get('id'));
+
+ self.get('files').loadFile(updatedModel.get('queryFile')).then(function (file) {
+ file.set('fileContent', content);
+ file.save().then(function (updatedFile) {
+ self.removeObject(query);
+ self.pushObject(updatedFile);
+ self.set('currentQuery', updatedFile);
+
+ defer.resolve();
+ }, function (err) {
+ defer.reject(err);
+ });
+ }, function (err) {
+ defer.reject(err);
+ });
+ } else {
+ query.set('fileContent', content);
+ query.save().then(function () {
+ self.toggleProperty('tabUpdated');
+ defer.resolve();
+ }, function (err) {
+ defer.reject(err);
+ });
+ }
+ }, function (err) {
+ defer.reject(err);
+ });
+
+ return defer.promise;
+ },
+
+ convertTabToJob: function (model, job) {
+ var defer = Ember.RSVP.defer(),
+ oldQuery = this.getQueryForModel(model),
+ tab = this.getTabForModel(model),
+ jobId = job.get('id'),
+ self = this;
+
+ tab.set('id', job.get('id'));
+ tab.set('type', constants.namingConventions.job);
+ tab.set('path', constants.namingConventions.subroutes.historyQuery);
+
+ this.get('files').loadFile(job.get('queryFile')).then(function (file) {
+ //replace old model representing file to reflect model update to job
+ if (self.keepOriginalQuery(jobId)) {
+ file.set('fileContent', oldQuery.get('fileContent'));
+ }
+
+ self.removeObject(oldQuery);
+ self.pushObject(file);
+
+ defer.resolve();
+ }, function (err) {
+ defer.reject(err);
+ });
+
+ return defer.promise;
+ },
+
+ keepOriginalQuery: function (jobId) {
+ var selected = this.get('highlightedText');
+ var hasQueryParams = this.get('index.queryParams.length');
+ var hasSettings = this.get('settings').hasSettings(jobId);
+
+ if ( selected && selected[0] !== "" ||
+ hasQueryParams ||
+ hasSettings ) {
+ return true;
+ }
+
+ return false;
+ },
+
+ actions: {
+ removeQueryTab: function (tab) {
+ var self = this,
+ defer,
+ remainingTabs = this.get('queryTabs').without(tab),
+ lastTab = remainingTabs.get('lastObject'),
+ closeTab = function () {
+ self.set('queryTabs', remainingTabs);
+
+ //remove cached results set
+ if (tab.type === constants.namingConventions.job) {
+ self.get('jobResults').clearCachedResultsSet(tab.id);
+ self.get('jobExplain').clearCachedExplainSet(tab.id);
+ }
+
+ if (lastTab.type === constants.namingConventions.job) {
+ self.transitionToRoute(constants.namingConventions.subroutes.historyQuery, lastTab.id);
+ } else {
+ self.transitionToRoute(constants.namingConventions.subroutes.savedQuery, lastTab.id);
+ }
+ };
+
+ this.store.find(tab.type, tab.id).then(function (model) {
+ var query = self.getQueryForModel(model);
+
+ if ((model.get('isNew') && !query.get('fileContent')) ||
+ (!model.get('isNew') && !query.get('isDirty'))) {
+ closeTab();
+ } else {
+ defer = Ember.RSVP.defer();
+ self.send('openModal',
+ 'modal-save',
+ {
+ heading: "modals.save.saveBeforeCloseHeading",
+ text: model.get('title'),
+ defer: defer
+ });
+
+ defer.promise.then(function (text) {
+ model.set('title', text);
+ self.save(model, query).then(function () {
+ closeTab();
+ });
+ }, function () {
+ model.rollback();
+ closeTab();
+ });
+ }
+ });
+ },
+
+ getColumnsForAutocomplete: function (tableName, callback) {
+ this.get('databases').getAllColumns(tableName).then(function () {
+ callback();
+ });
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js
new file mode 100644
index 0000000..85730a4
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/queries.js
@@ -0,0 +1,107 @@
+/**
+ * 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 Ember from 'ember';
+import FilterableMixin from 'hive/mixins/filterable';
+import constants from 'hive/utils/constants';
+
+export default Ember.ArrayController.extend(FilterableMixin, {
+ needs: [ constants.namingConventions.routes.history ],
+
+ sortAscending: true,
+ sortProperties: [],
+
+ init: function () {
+ var oneYearAgo = new Date();
+ oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
+
+ this._super();
+
+ this.set('columns', Ember.ArrayProxy.create({ content: Ember.A([
+ Ember.Object.create({
+ caption: "columns.shortQuery",
+ property: 'shortQuery',
+ link: constants.namingConventions.subroutes.savedQuery
+ }),
+ Ember.Object.create({
+ caption: "columns.title",
+ property: 'title',
+ link: constants.namingConventions.subroutes.savedQuery
+ }),
+ Ember.Object.create({
+ caption: "columns.database",
+ property: 'dataBase',
+ link: constants.namingConventions.subroutes.savedQuery
+ }),
+ Ember.Object.create({
+ caption: "columns.owner",
+ property: 'owner',
+ link: constants.namingConventions.subroutes.savedQuery
+ })
+ ])}));
+ },
+
+ //row buttons
+ links: [
+ "buttons.history",
+ "buttons.delete"
+ ],
+
+ model: function () {
+ var queries = this.get('queries');
+ queries = queries ? queries.filterBy('isNew', false) : queries;
+
+ return this.filter(queries);
+ }.property('queries', 'filters.@each'),
+
+ actions: {
+ executeAction: function (action, savedQuery) {
+ switch (action) {
+ case "buttons.history":
+ this.get('controllers.' + constants.namingConventions.routes.history).filterBy('queryId', savedQuery.get('id'), true);
+ this.transitionToRoute(constants.namingConventions.routes.history);
+ break;
+ case "buttons.delete":
+ var defer = Ember.RSVP.defer();
+ this.send('openModal',
+ 'modal-delete',
+ {
+ heading: "modals.delete.heading",
+ text: "modals.delete.message",
+ defer: defer
+ });
+
+ defer.promise.then(function () {
+ savedQuery.destroyRecord();
+ });
+
+ break;
+ }
+ },
+
+ sort: function (property) {
+ //if same column has been selected, toggle flag, else default it to true
+ if (this.get('sortProperties').objectAt(0) === property) {
+ this.set('sortAscending', !this.get('sortAscending'));
+ } else {
+ this.set('sortAscending', true);
+ this.set('sortProperties', [ property ]);
+ }
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js
new file mode 100644
index 0000000..51101c5
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/settings.js
@@ -0,0 +1,150 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.ArrayController.extend({
+ needs: [
+ constants.namingConventions.index,
+ constants.namingConventions.openQueries
+ ],
+
+ index: Ember.computed.alias('controllers.' + constants.namingConventions.index),
+ openQueries: Ember.computed.alias('controllers.' + constants.namingConventions.openQueries),
+
+ showSettingsOverlay: false,
+
+ querySettings: function () {
+ var currentId = this.get('index.model.id');
+ return this.findBy('id', currentId);
+ }.property('model.[]', 'index.model.id'),
+
+ updateSettingsId: function (oldId, newId) {
+ this.filterBy('id', oldId).setEach('id', newId);
+ },
+
+ getSettingsString: function (id) {
+ var currentId = id ? id : this.get('index.model.id');
+
+ var querySettings = this.findBy('id', currentId);
+
+ if (!querySettings) {
+ return "";
+ }
+
+ var settings = querySettings.get('settings').map(function (setting) {
+ return 'set %@ = %@;'.fmt(setting.key, setting.value);
+ });
+
+ if (querySettings.get('runOnTez')) {
+ settings.push('set %@ = tez;'.fmt(constants.settings.executionEngine));
+ }
+
+ return settings.join("\n");
+ },
+
+ hasSettings: function (id) {
+ id = id ? id : this.get('index.model.id');
+ var settings = this.findBy('id', id);
+
+ return settings && settings.get('settings.length');
+ },
+
+ parseQuerySettings: function () {
+ var id = this.get('index.model.id');
+ var query = this.get('openQueries.currentQuery');
+ var content = query.get('fileContent');
+ var runOnTez = false;
+
+
+ var regex = new RegExp(/^set\s+[\w-.]+(\s+|\s?)=(\s+|\s?)[\w-.]+(\s+|\s?);/gim);
+ var settings = content.match(regex);
+
+ if (!settings) {
+ return;
+ }
+
+ query.set('fileContent', content.replace(regex, '').trim());
+ settings = settings.map(function (setting) {
+ var KV = setting.split('=');
+
+ return {
+ key: KV[0].replace('set', '').trim(),
+ value: KV[1].replace(';', '').trim()
+ };
+ });
+
+ // remove runOnTez from settings
+ settings = settings.findBy('key', constants.settings.executionEngine).without(false);
+
+ this.setSettingForQuery(id, settings, !!runOnTez);
+ }.observes('openQueries.currentQuery', 'openQueries.tabUpdated'),
+
+ setSettingForQuery: function (id, settings, runOnTez) {
+ var querySettings = this.findBy('id', id);
+
+ if (!querySettings) {
+ this.pushObject(Ember.Object.create({
+ id: id,
+ settings: settings,
+ runOnTez: runOnTez
+ }));
+ } else {
+ querySettings.setProperties({
+ 'settings': settings,
+ 'runOnTez': runOnTez
+ });
+ }
+ },
+
+ createSettingsForQuery: function () {
+ var currentId = this.get('index.model.id');
+
+ if (!this.findBy('id', currentId)) {
+ this.pushObject(Ember.Object.create({
+ id: currentId,
+ settings: [],
+ runOnTez: false
+ }));
+ }
+ },
+
+ actions: {
+ toggleOverlay: function () {
+ // create a setting object if its not already there
+ this.createSettingsForQuery();
+ this.toggleProperty('showSettingsOverlay');
+ },
+
+ add: function () {
+ var currentId = this.get('index.model.id'),
+ querySettings = this.findBy('id', currentId);
+
+ querySettings.settings.pushObject(Ember.Object.create({
+ key: '',
+ value: ''
+ }));
+ },
+
+ remove: function (setting) {
+ var currentId = this.get('index.model.id');
+ this.findBy('id', currentId).settings.removeObject(setting);
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/tables.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/tables.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/tables.js
new file mode 100644
index 0000000..1d773e7
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/tables.js
@@ -0,0 +1,93 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.ObjectController.extend({
+ pageCount: 10,
+
+ init: function () {
+ this._super();
+
+ var databaseAdapter = this.container.lookup('adapter:database');
+ var baseUrl = databaseAdapter.buildURL() + '/' +
+ databaseAdapter.pathForType(constants.namingConventions.database) + '/';
+
+ this.set('baseUrl', baseUrl);
+ },
+
+ getTablesPage: function (database, searchTerm, firstSearchPage) {
+ var defer = Ember.RSVP.defer(),
+ url = this.get('baseUrl') +
+ database.get('name') +
+ '/table.page?count=';
+
+ url += this.get('pageCount');
+
+ if (searchTerm) {
+ url += '&searchId=searchTables' + '&like=' + searchTerm;
+
+ if (firstSearchPage) {
+ url += '&first=true';
+ }
+ } else if (!database.get('tables.length')) {
+ url += '&first=true';
+ }
+
+ Ember.$.getJSON(url).then(function (data) {
+ var tables;
+
+ tables = data.rows.map(function (row) {
+ return Ember.Object.create({
+ name: row[0]
+ });
+ });
+
+ defer.resolve({
+ tables: tables,
+ hasNext: data.hasNext
+ });
+ }, function (err) {
+ defer.reject(err);
+ });
+
+ return defer.promise;
+ },
+
+ getTables: function (databaseName) {
+ var defer = Ember.RSVP.defer(),
+ url = this.get('baseUrl') +
+ databaseName +
+ '/table';
+
+ Ember.$.getJSON(url).then(function (data) {
+ var tables = data.tables.map(function (table) {
+ return Ember.Object.create({
+ name: table
+ });
+ });
+
+ defer.resolve(tables);
+ }, function (err) {
+ defer.reject(err);
+ });
+
+ return defer.promise;
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js
new file mode 100644
index 0000000..657743c
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udf.js
@@ -0,0 +1,138 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.ObjectController.extend({
+ needs: [ constants.namingConventions.udfs, constants.namingConventions.fileResources ],
+
+ columns: Ember.computed.alias('controllers.' + constants.namingConventions.udfs + '.columns'),
+ links: Ember.computed.alias('controllers.' + constants.namingConventions.udfs + '.links'),
+ fileResources: Ember.computed.alias('controllers.' + constants.namingConventions.fileResources),
+
+ init: function () {
+ this._super();
+
+ if (this.get('model.isNew')) {
+ this.set('isEditing', true);
+ }
+
+ // we need this because model.rollback doesnt roll back secondary relations
+ this.set('fileBackup', this.get('model.fileResource'));
+ },
+
+ actions: {
+ executeAction: function (action) {
+ switch (action) {
+ case 'buttons.edit':
+ this.set('isEditing', true);
+ break;
+ case 'buttons.delete':
+ var defer = Ember.RSVP.defer(),
+ self = this;
+
+ this.send('openModal',
+ 'modal-delete',
+ {
+ heading: Ember.I18n.translations.modals.delete.heading,
+ text: Ember.I18n.translations.modals.delete.message,
+ defer: defer
+ });
+
+ defer.promise.then(function () {
+ self.get('model').destroyRecord();
+ });
+ break;
+ }
+ },
+
+ save: function () {
+ var self = this,
+ saveUdf = function () {
+ self.get('model').save().then(function (updatedModel) {
+ self.set('isEditing', false);
+ self.set('isEditingResource', false);
+ self.set('fileBackup', updatedModel.get('fileResource'));
+ });
+ };
+
+ //replace with a validation system if needed.
+ if (!this.get('model.name') || !this.get('model.classname')) {
+ return;
+ }
+
+ this.get('model.fileResource').then(function (file) {
+ if (file) {
+ if (!file.get('name') || !file.get('path')) {
+ return;
+ }
+
+ file.save().then(function () {
+ saveUdf();
+ });
+ } else {
+ saveUdf();
+ }
+ });
+ },
+
+ cancel: function () {
+ var self = this;
+
+ this.set('isEditing', false);
+ this.set('isEditingResource', false);
+
+ this.model.get('fileResource').then(function (file) {
+ if (file) {
+ file.rollback();
+ }
+
+ self.model.rollback();
+ self.model.set('fileResource', self.get('fileBackup'));
+ });
+ },
+
+ addFileResource: function () {
+ var file = this.store.createRecord(constants.namingConventions.fileResource);
+ this.set('isEditingResource', true);
+ this.model.set('fileResource', file);
+ },
+
+ editFileResource: function (file) {
+ this.set('isEditingResource', true);
+ this.model.set('fileResource', file);
+ },
+
+ removeFileResource: function (file) {
+ var defer = Ember.RSVP.defer();
+
+ this.send('openModal',
+ 'modal-delete',
+ {
+ heading: Ember.I18n.translations.modals.delete.heading,
+ text: Ember.I18n.translations.modals.delete.message,
+ defer: defer
+ });
+
+ defer.promise.then(function () {
+ file.destroyRecord();
+ });
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udfs.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udfs.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udfs.js
new file mode 100644
index 0000000..52faba1
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/udfs.js
@@ -0,0 +1,69 @@
+/**
+ * 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 Ember from 'ember';
+import FilterableMixin from 'hive/mixins/filterable';
+import constants from 'hive/utils/constants';
+
+export default Ember.ArrayController.extend(FilterableMixin, {
+ itemController: constants.namingConventions.udf,
+
+ sortAscending: true,
+ sortProperties: [],
+
+ init: function () {
+ this._super();
+
+ this.set('columns', Ember.ArrayProxy.create({ content: Ember.A([
+ Ember.Object.create({
+ caption: 'placeholders.udfs.name',
+ property: 'name'
+ }),
+ Ember.Object.create({
+ caption: 'placeholders.udfs.className',
+ property: 'classname'
+ })
+ ])}));
+ },
+
+ //row buttons
+ links: [
+ 'buttons.edit',
+ 'buttons.delete'
+ ],
+
+ model: function () {
+ return this.filter(this.get('udfs'));
+ }.property('udfs', 'filters.@each'),
+
+ actions: {
+ sort: function (property) {
+ //if same column has been selected, toggle flag, else default it to true
+ if (this.get('sortProperties').objectAt(0) === property) {
+ this.set('sortAscending', !this.get('sortAscending'));
+ } else {
+ this.set('sortAscending', true);
+ this.set('sortProperties', [ property ]);
+ }
+ },
+
+ add: function () {
+ this.store.createRecord(constants.namingConventions.udf);
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/.gitkeep b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/.gitkeep
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/code-helper.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/code-helper.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/code-helper.js
new file mode 100644
index 0000000..327636f
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/code-helper.js
@@ -0,0 +1,28 @@
+/**
+ * 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 Ember from 'ember';
+
+export function code(text) {
+ text = Ember.Handlebars.Utils.escapeExpression(text);
+ text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
+
+ return new Ember.Handlebars.SafeString('<code>' + text + '</code>');
+}
+
+export default Ember.Handlebars.makeBoundHelper(code);
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/date-binding.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/date-binding.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/date-binding.js
new file mode 100644
index 0000000..4baeca8
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/date-binding.js
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* globals moment */
+
+import Ember from 'ember';
+
+export function pathBinding(data, key) {
+ return moment(data.get(key)).fromNow();
+}
+
+export default Ember.Handlebars.makeBoundHelper(pathBinding);
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/log-helper.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/log-helper.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/log-helper.js
new file mode 100644
index 0000000..c603c60
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/log-helper.js
@@ -0,0 +1,28 @@
+/**
+ * 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 Ember from 'ember';
+
+export function log(text) {
+ text = Ember.Handlebars.Utils.escapeExpression(text);
+ text = text.replace(/(\r\n|\n|\r)/gm, '<br>');
+
+ return new Ember.Handlebars.SafeString(text);
+}
+
+export default Ember.Handlebars.makeBoundHelper(log);
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/path-binding.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/path-binding.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/path-binding.js
new file mode 100644
index 0000000..e53772e
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/path-binding.js
@@ -0,0 +1,29 @@
+/**
+ * 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 Ember from 'ember';
+
+export function pathBinding(data, key) {
+ if (!data || !key) {
+ return;
+ }
+
+ return data.get ? data.get(key) : data[key];
+}
+
+export default Ember.Handlebars.makeBoundHelper(pathBinding);
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/tb-helper.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/tb-helper.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/tb-helper.js
new file mode 100644
index 0000000..fcce185
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/helpers/tb-helper.js
@@ -0,0 +1,33 @@
+/**
+ * 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 Ember from 'ember';
+
+export function tb(key, data) {
+ var path = data.get ? data.get(key) : data[key];
+
+ if (!path && key) {
+ return Ember.I18n.t(key);
+ }
+
+ if (path) {
+ return Ember.I18n.t(path);
+ }
+}
+
+export default Ember.Handlebars.makeBoundHelper(tb);
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html b/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html
new file mode 100644
index 0000000..4f609bd
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/index.html
@@ -0,0 +1,38 @@
+<!--
+ * 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.
+-->
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <title>Hive</title>
+ <meta name="description" content="">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+
+ {{content-for 'head'}}
+
+ <link rel="stylesheet" href="assets/vendor.css">
+ <link rel="stylesheet" href="assets/hive.css">
+ </head>
+ <body>
+ {{content-for 'body'}}
+
+ <script src="assets/vendor.js"></script>
+ <script src="assets/hive.js"></script>
+ </body>
+</html>
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js
new file mode 100644
index 0000000..0040307
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/initializers/i18n.js
@@ -0,0 +1,160 @@
+/**
+ * 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 Ember from 'ember';
+
+var TRANSLATIONS;
+
+export default {
+ name: 'i18n',
+ initialize: function() {
+ Ember.ENV.I18N_COMPILE_WITHOUT_HANDLEBARS = true;
+ Ember.FEATURES.I18N_TRANSLATE_HELPER_SPAN = false;
+ Ember.I18n.translations = TRANSLATIONS;
+ Ember.TextField.reopen(Ember.I18n.TranslateableAttributes);
+ }
+};
+
+TRANSLATIONS = {
+ alerts: {
+ errors: {
+ save: {
+ query: "Error when trying to execute the query",
+ results: "Error when trying to save the results."
+ },
+ get: {
+ tables: 'Error when trying to retrieve the tables for the selected database',
+ columns: 'Error when trying to retrieve the table columns.'
+ }
+ }
+ },
+ modals: {
+ delete: {
+ heading: 'Confirm deletion',
+ message: 'Are you sure you want to delete this item?',
+ emptyQueryMessage: "Your query is empty. Do you want to delete this item?"
+ },
+
+ save: {
+ heading: 'Saving item',
+ saveBeforeCloseHeading: "Save item before closing?",
+ message: 'Enter name:'
+ }
+ },
+ titles: {
+ database: 'Database Explorer',
+ explorer: 'Databases',
+ results: 'Search Results',
+ settings: 'Database Settings',
+ query: {
+ editor: 'Query Editor',
+ process: 'Query Process Results',
+ parameters: 'Parameters'
+ },
+ download: 'Save results...'
+ },
+ placeholders: {
+ search: {
+ tables: 'Search tables...',
+ columns: 'Search columns in result tables...'
+ },
+ select: {
+ database: 'Select Database...',
+ udfs: 'Insert udfs',
+ file: 'Select File Resource...',
+ noFileResource: '(no file)'
+ },
+ fileResource: {
+ name: "resource name",
+ path: "resource path"
+ },
+ udfs: {
+ name: 'udf name',
+ className: 'udf class name',
+ path: "resource path",
+ database: 'Select Database...'
+ },
+ settings: {
+ key: 'mapred.reduce.tasks',
+ value: '1'
+ }
+ },
+ menus: {
+ query: 'Query',
+ savedQueries: 'Saved Queries',
+ history: 'History',
+ udfs: 'UDFs',
+ logs: 'Logs',
+ results: 'Results',
+ explain: 'Explain'
+ },
+ columns: {
+ id: 'id',
+ shortQuery: 'preview',
+ fileResource: 'file resource',
+ title: 'title',
+ database: 'database',
+ owner: 'owner',
+ user: 'user',
+ date: 'date submitted',
+ duration: 'duration',
+ status: 'status',
+ expand: '',
+ actions: ''
+ },
+ buttons: {
+ addItem: 'Add new item...',
+ insert: 'Insert',
+ delete: 'Delete',
+ cancel: 'Cancel',
+ edit: 'Edit',
+ execute: 'Execute',
+ explain: 'Explain',
+ saveAs: 'Save as...',
+ save: 'Save',
+ newQuery: 'New Query',
+ newUdf: 'New UDF',
+ history: 'History',
+ ok: 'OK',
+ stopJob: 'Stop execution',
+ close: 'Close',
+ clearFilters: 'Clear filters',
+ expand: 'Expand message',
+ collapse: 'Collapse message',
+ previousPage: 'previous',
+ nextPage: 'next',
+ loadMore: 'Load more...',
+ saveHdfs: 'Save to HDFS',
+ saveCsv: 'Download as CSV',
+ runOnTez: 'Run on Tez'
+ },
+ labels: {
+ noTablesMatches: 'No tables matches for'
+ },
+ popover: {
+ queryEditorHelp: {
+ title: "Did you know?",
+ content: {
+ line1: "Press CTRL + Space to autocomplete",
+ line2: "You can execute queries with multiple SQL statements delimited by a semicolon ';'",
+ line3: "You can highlight and run a fragment of a query"
+ }
+ },
+ add: 'Add'
+ }
+};
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/mixins/filterable.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/mixins/filterable.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/mixins/filterable.js
new file mode 100644
index 0000000..55d4593
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/mixins/filterable.js
@@ -0,0 +1,104 @@
+/**
+ * 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 Ember from 'ember';
+import utils from 'hive/utils/functions';
+
+export default Ember.Mixin.create({
+ init: function () {
+ this._super();
+ this.clearFilters();
+ },
+
+ filter: function (items) {
+ var self = this;
+
+ if (items && this.get('filters.length')) {
+ items = items.filter(function (item) {
+ return self.get('filters').every(function (filter) {
+ var propValue = item.get(filter.property);
+
+ if (!!filter.value) {
+ if (filter.min !== undefined && filter.max !== undefined) {
+ if (utils.isInteger(propValue)) {
+ return +propValue >= +filter.min && +propValue <= +filter.max;
+ } else if (utils.isDate(propValue)) {
+ return propValue >= filter.min && propValue <= filter.max;
+ } else {
+ return false;
+ }
+ } else if (filter.exactMatch) {
+ return propValue == filter.value;
+ } else {
+ return propValue && propValue.toLowerCase().indexOf(filter.value.toLowerCase()) > -1;
+ }
+ }
+
+ return false;
+ });
+ });
+ }
+
+ return items;
+ },
+
+ updateFilters: function (property, filterValue, exactMatch) {
+ var addFilter = function () {
+ this.get('filters').pushObject(Ember.Object.create({
+ property: property,
+ exactMatch: exactMatch,
+ min: filterValue.min,
+ max: filterValue.max,
+ value: filterValue
+ }));
+ };
+
+ var existentFilter = this.get('filters').find(function (filter) {
+ return filter.property === property;
+ });
+
+ if (existentFilter) {
+ if (filterValue) {
+ //remove and add again for triggering collection change thus avoiding to add observers on individual properties of a filter
+ this.get('filters').removeObject(existentFilter);
+ addFilter.apply(this);
+ } else {
+ //ensures removal of the filterValue when it's an empty string
+ this.set('filters', this.get('filters').without(existentFilter));
+ }
+ } else {
+ addFilter.apply(this);
+ }
+ },
+
+ clearFilters: function () {
+ if (!this.get('filters') || this.get('filters.length')) {
+ this.set('filters', Ember.A());
+ }
+ },
+
+ actions: {
+ filter: function (property, filterValue) {
+ this.updateFilters(property, filterValue);
+ },
+
+ clearFilters: function () {
+ this.clearFilters();
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/mixins/sortable.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/mixins/sortable.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/mixins/sortable.js
new file mode 100644
index 0000000..f766032
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/mixins/sortable.js
@@ -0,0 +1,31 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.SortableMixin.reopen({
+ sort: function (property) {
+ //if same column has been selected, toggle flag, else default it to true
+ if (this.get('sortProperties').objectAt(0) === property) {
+ this.set('sortAscending', !this.get('sortAscending'));
+ } else {
+ this.set('sortAscending', true);
+ this.set('sortProperties', [ property ]);
+ }
+ }
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/models/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/.gitkeep b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/.gitkeep
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/models/database.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/database.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/database.js
new file mode 100644
index 0000000..11ee144
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/database.js
@@ -0,0 +1,25 @@
+/**
+ * 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 DS from 'ember-data';
+
+var Database = DS.Model.extend({
+ name: DS.attr()
+});
+
+export default Database;
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file-resource.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file-resource.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file-resource.js
new file mode 100644
index 0000000..47f4911
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file-resource.js
@@ -0,0 +1,25 @@
+/**
+ * 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 DS from 'ember-data';
+
+export default DS.Model.extend({
+ name: DS.attr(),
+ path: DS.attr(),
+ owner: DS.attr()
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js
new file mode 100644
index 0000000..f8f1b9b
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/file.js
@@ -0,0 +1,26 @@
+/**
+ * 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 DS from 'ember-data';
+
+export default DS.Model.extend({
+ fileContent: DS.attr(),
+ hasNext: DS.attr(),
+ page: DS.attr,
+ pageCount: DS.attr()
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js
new file mode 100644
index 0000000..95e5d57
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/job.js
@@ -0,0 +1,32 @@
+/**
+ * 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 DS from 'ember-data';
+
+export default DS.Model.extend({
+ title: DS.attr('string'),
+ queryId: DS.attr(),
+ queryFile: DS.attr('string'),
+ owner: DS.attr('string'),
+ dataBase: DS.attr('string'),
+ duration: DS.attr(),
+ status: DS.attr('string'),
+ dateSubmitted: DS.attr('date'),
+ forcedContent: DS.attr('string'),
+ logFile: DS.attr('string')
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/models/saved-query.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/saved-query.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/saved-query.js
new file mode 100644
index 0000000..44536af
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/saved-query.js
@@ -0,0 +1,29 @@
+/**
+ * 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 DS from 'ember-data';
+
+var Model = DS.Model.extend({
+ dataBase: DS.attr('string'),
+ title: DS.attr('string'),
+ queryFile: DS.attr('string'),
+ owner: DS.attr('string'),
+ shortQuery: DS.attr('string')
+});
+
+export default Model;
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/models/udf.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/models/udf.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/udf.js
new file mode 100644
index 0000000..c64221e
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/models/udf.js
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import DS from 'ember-data';
+import constants from 'hive/utils/constants';
+
+export default DS.Model.extend({
+ name: DS.attr(),
+ classname: DS.attr(),
+ fileResource: DS.belongsTo(constants.namingConventions.fileResource, { async: true }),
+ owner: DS.attr()
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/router.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/router.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/router.js
new file mode 100644
index 0000000..244bc58
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/router.js
@@ -0,0 +1,47 @@
+ /**
+ * 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 Ember from 'ember';
+import config from './config/environment';
+import constants from 'hive/utils/constants';
+
+var Router = Ember.Router.extend({
+ location: config.locationType
+});
+
+Router.map(function() {
+ var savedQueryPath = constants.namingConventions.routes.queries + '/:' + constants.namingConventions.savedQuery + '_id';
+ var historyQueryPath = constants.namingConventions.routes.history + '/:' + constants.namingConventions.job + '_id';
+
+ this.route(constants.namingConventions.routes.queries);
+ this.route(constants.namingConventions.routes.history);
+ this.route(constants.namingConventions.routes.udfs);
+
+ this.resource(constants.namingConventions.routes.index, { path: '/' }, function () {
+ this.route(constants.namingConventions.routes.savedQuery, { path: savedQueryPath});
+ this.route(constants.namingConventions.routes.historyQuery, { path: historyQueryPath}, function () {
+ this.route(constants.namingConventions.routes.logs);
+ this.route(constants.namingConventions.routes.results);
+ this.route(constants.namingConventions.routes.explain);
+ });
+ });
+
+ this.route('loading');
+});
+
+export default Router;
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/.gitkeep b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/.gitkeep
new file mode 100644
index 0000000..e69de29
[10/12] ambari git commit: AMBARI-9676. Initial Hive View Submission
to Contrib (alexantonenko)
Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java
new file mode 100644
index 0000000..8f143e7
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ConnectionController.java
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.client.Connection;
+import org.apache.ambari.view.hive.client.ConnectionPool;
+import org.apache.ambari.view.hive.client.HiveClientException;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.apache.hive.service.cli.thrift.TOperationHandle;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ConnectionController {
+ private ViewContext context;
+ private Connection connection;
+ private OperationHandleControllerFactory operationHandleControllerFactory;
+
+ private ConnectionController(ViewContext context) {
+ this.context = context;
+ connection = ConnectionPool.getConnection(context);
+ operationHandleControllerFactory = OperationHandleControllerFactory.getInstance(context);
+ }
+
+ private static Map<String, ConnectionController> viewSingletonObjects = new HashMap<String, ConnectionController>();
+ public static ConnectionController getInstance(ViewContext context) {
+ if (!viewSingletonObjects.containsKey(context.getInstanceName()))
+ viewSingletonObjects.put(context.getInstanceName(), new ConnectionController(context));
+ return viewSingletonObjects.get(context.getInstanceName());
+ }
+
+ public void selectDatabase(String database) {
+ executeQuery("use " + database + ";");
+ }
+
+ public OperationHandleController executeQuery(String cmd) {
+ TOperationHandle operationHandle = null;
+ try {
+ operationHandle = connection.executeAsync(cmd);
+ } catch (HiveClientException e) {
+ throw new ServiceFormattedException(e.toString(), e);
+ }
+ return operationHandleControllerFactory.createControllerForHandle(operationHandle);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/FileResourceShort.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/FileResourceShort.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/FileResourceShort.java
new file mode 100644
index 0000000..776bf64
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/FileResourceShort.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.apache.commons.beanutils.BeanUtils;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+
+public class FileResourceShort {
+ public FileResourceShort() {}
+ public FileResourceShort(Map<String, Object> stringObjectMap) throws InvocationTargetException, IllegalAccessException {
+ BeanUtils.populate(this, stringObjectMap);
+ }
+
+ private Integer id;
+ private String path;
+
+ public FileResourceShort(Integer id, String path) {
+ this.id = id;
+ this.path = path;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Job.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Job.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Job.java
new file mode 100644
index 0000000..e6f7a63
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/Job.java
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.apache.ambari.view.hive.persistence.utils.Indexed;
+import org.apache.ambari.view.hive.persistence.utils.PersonalResource;
+
+import java.io.Serializable;
+
+/**
+ * Interface for Job bean to create Proxy for it
+ */
+public interface Job extends Serializable,Indexed,PersonalResource {
+ public static final String JOB_STATE_UNKNOWN = "Unknown";
+ public static final String JOB_STATE_INITIALIZED = "Initialized";
+ public static final String JOB_STATE_RUNNING = "Running";
+ public static final String JOB_STATE_FINISHED = "Finished";
+ public static final String JOB_STATE_CANCELED = "Canceled";
+ public static final String JOB_STATE_CLOSED = "Closed";
+ public static final String JOB_STATE_ERROR = "Error";
+ public static final String JOB_STATE_PENDING = "Pending";
+
+ Integer getId();
+
+ void setId(Integer id);
+
+ String getOwner();
+
+ void setOwner(String owner);
+
+ String getTitle();
+
+ void setTitle(String title);
+
+ String getQueryFile();
+
+ void setQueryFile(String queryFile);
+
+ Long getDateSubmitted();
+
+ void setDateSubmitted(Long dateSubmitted);
+
+ Long getDuration();
+
+ void setDuration(Long duration);
+
+ String getStatus();
+
+ void setStatus(String status);
+
+ String getForcedContent();
+
+ void setForcedContent(String forcedContent);
+
+ Integer getQueryId();
+
+ void setQueryId(Integer queryId);
+
+ String getStatusDir();
+
+ void setStatusDir(String statusDir);
+
+ String getDataBase();
+
+ void setDataBase(String dataBase);
+
+ String getLogFile();
+
+ void setLogFile(String logFile);
+
+ String getConfFile();
+
+ void setConfFile(String confFile);
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobController.java
new file mode 100644
index 0000000..3d5189e
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobController.java
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.apache.ambari.view.hive.client.Cursor;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+
+public interface JobController {
+ void submit();
+
+ void cancel() throws ItemNotFound;
+
+ Job getJob();
+
+ /**
+ * Use carefully. Returns unproxied bean object
+ * @return unproxied bean object
+ */
+ Job getJobPOJO();
+
+ Cursor getResults() throws ItemNotFound;
+
+ void afterCreation();
+
+ void onRead();
+
+ boolean isModified();
+
+ void clearModified();
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobControllerFactory.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobControllerFactory.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobControllerFactory.java
new file mode 100644
index 0000000..f6ec5b1
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobControllerFactory.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.apache.ambari.view.ViewContext;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class JobControllerFactory {
+ private ViewContext context;
+
+ private JobControllerFactory(ViewContext context) {
+ this.context = context;
+ }
+
+ private static Map<String, JobControllerFactory> viewSingletonObjects = new HashMap<String, JobControllerFactory>();
+ public static JobControllerFactory getInstance(ViewContext context) {
+ if (!viewSingletonObjects.containsKey(context.getInstanceName()))
+ viewSingletonObjects.put(context.getInstanceName(), new JobControllerFactory(context));
+ return viewSingletonObjects.get(context.getInstanceName());
+ }
+
+ public JobController createControllerForJob(Job job) {
+ return new JobControllerImpl(context, job);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobControllerImpl.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobControllerImpl.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobControllerImpl.java
new file mode 100644
index 0000000..a9f315c
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobControllerImpl.java
@@ -0,0 +1,326 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.client.*;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.resources.savedQueries.SavedQuery;
+import org.apache.ambari.view.hive.resources.savedQueries.SavedQueryResourceManager;
+import org.apache.ambari.view.hive.utils.*;
+import org.apache.ambari.view.hive.utils.HdfsApi;
+import org.apache.ambari.view.hive.utils.HdfsUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.lang.reflect.Proxy;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+public class JobControllerImpl implements JobController, ModifyNotificationDelegate {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(JobControllerImpl.class);
+
+ private ViewContext context;
+ private Job jobUnproxied;
+ private Job job;
+ private boolean modified;
+
+ private OperationHandleControllerFactory operationHandleControllerFactory;
+ private ConnectionController hiveSession;
+ private SavedQueryResourceManager savedQueryResourceManager;
+
+ /**
+ * JobController constructor
+ * Warning: Create JobControllers ONLY using JobControllerFactory!
+ */
+ public JobControllerImpl(ViewContext context, Job job) {
+ this.context = context;
+ setJobPOJO(job);
+ operationHandleControllerFactory = OperationHandleControllerFactory.getInstance(context);
+ hiveSession = ConnectionController.getInstance(context);
+ savedQueryResourceManager = SavedQueryResourceManager.getInstance(context);
+ }
+
+ public String getQueryForJob() {
+ FilePaginator paginator = new FilePaginator(job.getQueryFile(), context);
+ String query;
+ try {
+ query = paginator.readPage(0); //warning - reading only 0 page restricts size of query to 1MB
+ } catch (IOException e) {
+ throw new ServiceFormattedException("Error when reading file: " + e.toString(), e);
+ } catch (InterruptedException e) {
+ throw new ServiceFormattedException("Error when reading file: " + e.toString(), e);
+ }
+ return query;
+ }
+
+ private static final String DEFAULT_DB = "default";
+ public String getJobDatabase() {
+ if (job.getDataBase() != null) {
+ return job.getDataBase();
+ } else {
+ return DEFAULT_DB;
+ }
+ }
+
+ @Override
+ public void submit() {
+ setupHiveBeforeQueryExecute();
+
+ String query = getQueryForJob();
+ OperationHandleController handleController = hiveSession.executeQuery(query);
+
+ handleController.persistHandleForJob(job);
+ }
+
+ private void setupHiveBeforeQueryExecute() {
+ String database = getJobDatabase();
+ hiveSession.selectDatabase(database);
+ }
+
+ @Override
+ public void cancel() throws ItemNotFound {
+ OperationHandleController handle = operationHandleControllerFactory.getHandleForJob(job);
+ handle.cancel();
+ }
+
+ @Override
+ public void onRead() {
+ updateOperationStatus();
+ updateOperationLogs();
+
+ updateJobDuration();
+ }
+
+ public void updateOperationStatus() {
+ try {
+
+ OperationHandleController handle = operationHandleControllerFactory.getHandleForJob(job);
+ String status = handle.getOperationStatus();
+ job.setStatus(status);
+ LOG.debug("Status of job#" + job.getId() + " is " + job.getStatus());
+
+ } catch (NoOperationStatusSetException e) {
+ LOG.info("Operation state is not set for job#" + job.getId());
+
+ } catch (HiveErrorStatusException e) {
+ LOG.debug("Error updating status for job#" + job.getId() + ": " + e.getMessage());
+ job.setStatus(Job.JOB_STATE_UNKNOWN);
+
+ } catch (HiveClientException e) {
+ throw new ServiceFormattedException("Could not fetch job status " + job.getId(), e);
+
+ } catch (ItemNotFound itemNotFound) {
+ LOG.debug("No TOperationHandle for job#" + job.getId() + ", can't update status");
+ }
+ }
+
+ public void updateOperationLogs() {
+ try {
+ OperationHandleController handle = operationHandleControllerFactory.getHandleForJob(job);
+ String logs = handle.getLogs();
+
+// LogParser info = LogParser.parseLog(logs);
+
+ String logFilePath = job.getLogFile();
+ HdfsUtil.putStringToFile(context, logFilePath, logs);
+
+ } catch (HiveClientRuntimeException ex) {
+ LOG.error("Error while fetching logs: " + ex.getMessage());
+ } catch (ItemNotFound itemNotFound) {
+ LOG.debug("No TOperationHandle for job#" + job.getId() + ", can't read logs");
+ }
+ }
+
+ public boolean isJobEnded() {
+ String status = job.getStatus();
+ return status.equals(Job.JOB_STATE_FINISHED) || status.equals(Job.JOB_STATE_CANCELED) ||
+ status.equals(Job.JOB_STATE_CLOSED) || status.equals(Job.JOB_STATE_ERROR) ||
+ status.equals(Job.JOB_STATE_UNKNOWN); // Unknown is not finished, but polling makes no sense
+ }
+
+ @Override
+ public Job getJob() {
+ return job;
+ }
+
+ /**
+ * Use carefully. Returns unproxied bean object
+ * @return unproxied bean object
+ */
+ @Override
+ public Job getJobPOJO() {
+ return jobUnproxied;
+ }
+
+ public void setJobPOJO(Job jobPOJO) {
+ Job jobModifyNotificationProxy = (Job) Proxy.newProxyInstance(jobPOJO.getClass().getClassLoader(),
+ new Class[]{Job.class},
+ new ModifyNotificationInvocationHandler(jobPOJO, this));
+ this.job = jobModifyNotificationProxy;
+
+ this.jobUnproxied = jobPOJO;
+ }
+
+ @Override
+ public Cursor getResults() throws ItemNotFound {
+ OperationHandleController handle = operationHandleControllerFactory.getHandleForJob(job);
+ return handle.getResults();
+ }
+
+ @Override
+ public void afterCreation() {
+ setupStatusDirIfNotPresent();
+ setupQueryFileIfNotPresent();
+ setupLogFileIfNotPresent();
+
+ setCreationDate();
+ }
+
+ public void setupLogFileIfNotPresent() {
+ if (job.getLogFile() == null || job.getLogFile().isEmpty()) {
+ setupLogFile();
+ }
+ }
+
+ public void setupQueryFileIfNotPresent() {
+ if (job.getQueryFile() == null || job.getQueryFile().isEmpty()) {
+ setupQueryFile();
+ }
+ }
+
+ public void setupStatusDirIfNotPresent() {
+ if (job.getStatusDir() == null || job.getStatusDir().isEmpty()) {
+ setupStatusDir();
+ }
+ }
+
+ private static final long MillisInSecond = 1000L;
+
+ public void updateJobDuration() {
+ job.setDuration(System.currentTimeMillis() / MillisInSecond - job.getDateSubmitted());
+ }
+
+ public void setCreationDate() {
+ job.setDateSubmitted(System.currentTimeMillis() / MillisInSecond);
+ }
+
+
+
+ private void setupLogFile() {
+ LOG.debug("Creating log file for job#" + job.getId());
+
+ String logFile = job.getStatusDir() + "/" + "logs";
+ HdfsUtil.putStringToFile(context, logFile, "");
+
+ job.setLogFile(logFile);
+ LOG.debug("Log file for job#" + job.getId() + ": " + logFile);
+ }
+
+ private void setupStatusDir() {
+ String newDirPrefix = makeStatusDirectoryPrefix();
+ String newDir = HdfsUtil.findUnallocatedFileName(context, newDirPrefix, "");
+
+ job.setStatusDir(newDir);
+ LOG.debug("Status dir for job#" + job.getId() + ": " + newDir);
+ }
+
+ private String makeStatusDirectoryPrefix() {
+ String userScriptsPath = context.getProperties().get("jobs.dir");
+
+ if (userScriptsPath == null) { // TODO: move check to initialization code
+ String msg = "jobs.dir is not configured!";
+ LOG.error(msg);
+ throw new MisconfigurationFormattedException("jobs.dir");
+ }
+
+ String normalizedName = String.format("hive-job-%d", job.getId());
+ String timestamp = new SimpleDateFormat("yyyy-MM-dd_hh-mm").format(new Date());
+ return String.format(userScriptsPath +
+ "/%s-%s", normalizedName, timestamp);
+ }
+
+ private void setupQueryFile() {
+ String statusDir = job.getStatusDir();
+ assert statusDir != null : "setupStatusDir() should be called first";
+
+ String jobQueryFilePath = statusDir + "/" + "query.hql";
+
+ try {
+
+ if (job.getForcedContent() != null) {
+
+ HdfsUtil.putStringToFile(context, jobQueryFilePath, job.getForcedContent());
+ job.setForcedContent(""); // prevent forcedContent to be written to DB
+
+ }
+ else if (job.getQueryId() != null) {
+
+ String savedQueryFile = getRelatedSavedQueryFile();
+ HdfsApi.getInstance(context).copy(savedQueryFile, jobQueryFilePath);
+ job.setQueryFile(jobQueryFilePath);
+
+ } else {
+
+ throw new BadRequestFormattedException("queryId or forcedContent should be passed!", null);
+
+ }
+
+ } catch (IOException e) {
+ throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
+ } catch (InterruptedException e) {
+ throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
+ }
+ job.setQueryFile(jobQueryFilePath);
+
+ LOG.debug("Query file for job#" + job.getId() + ": " + jobQueryFilePath);
+ }
+
+ private String getRelatedSavedQueryFile() {
+ SavedQuery savedQuery;
+ try {
+ savedQuery = savedQueryResourceManager.read(job.getQueryId());
+ } catch (ItemNotFound itemNotFound) {
+ throw new BadRequestFormattedException("queryId not found!", itemNotFound);
+ }
+ return savedQuery.getQueryFile();
+ }
+
+ @Override
+ public boolean onModification(Object object) {
+ setModified(true);
+ return true;
+ }
+
+ @Override
+ public boolean isModified() {
+ return modified;
+ }
+
+ public void setModified(boolean modified) {
+ this.modified = modified;
+ }
+
+ @Override
+ public void clearModified() {
+ setModified(false);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobImpl.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobImpl.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobImpl.java
new file mode 100644
index 0000000..7d65957
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobImpl.java
@@ -0,0 +1,203 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.apache.commons.beanutils.PropertyUtils;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+
+/**
+ * Bean to represent saved query
+ */
+public class JobImpl implements Job {
+ private String title = null;
+ private String queryFile = null;
+ private String statusDir = null;
+ private Long dateSubmitted = 0L;
+ private Long duration = 0L;
+ private String status = JOB_STATE_UNKNOWN;
+ private String forcedContent = null;
+ private String dataBase = null;
+ private Integer queryId = null;
+
+ private Integer id = null;
+ private String owner = null;
+
+ private String logFile;
+ private String confFile;
+
+ public JobImpl() {}
+ public JobImpl(Map<String, Object> stringObjectMap) throws InvocationTargetException, IllegalAccessException {
+ for (Map.Entry<String, Object> entry : stringObjectMap.entrySet()) {
+ try {
+ PropertyUtils.setProperty(this, entry.getKey(), entry.getValue());
+ } catch (NoSuchMethodException e) {
+ //do nothing, skip
+ }
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Job)) return false;
+
+ JobImpl job = (JobImpl) o;
+
+ if (id != null ? !id.equals(job.id) : job.id != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return id != null ? id.hashCode() : 0;
+ }
+
+ @Override
+ public Integer getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getOwner() {
+ return owner;
+ }
+
+ @Override
+ public void setOwner(String owner) {
+ this.owner = owner;
+ }
+
+ @Override
+ public String getTitle() {
+ return title;
+ }
+
+ @Override
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ @Override
+ public String getQueryFile() {
+ return queryFile;
+ }
+
+ @Override
+ public void setQueryFile(String queryFile) {
+ this.queryFile = queryFile;
+ }
+
+ @Override
+ public Long getDateSubmitted() {
+ return dateSubmitted;
+ }
+
+ @Override
+ public void setDateSubmitted(Long dateSubmitted) {
+ this.dateSubmitted = dateSubmitted;
+ }
+
+ @Override
+ public Long getDuration() {
+ return duration;
+ }
+
+ @Override
+ public void setDuration(Long duration) {
+ this.duration = duration;
+ }
+
+ @Override
+ public String getStatus() {
+ return status;
+ }
+
+ @Override
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ @Override
+ public String getForcedContent() {
+ return forcedContent;
+ }
+
+ @Override
+ public void setForcedContent(String forcedContent) {
+ this.forcedContent = forcedContent;
+ }
+
+ @Override
+ public Integer getQueryId() {
+ return queryId;
+ }
+
+ @Override
+ public void setQueryId(Integer queryId) {
+ this.queryId = queryId;
+ }
+
+ @Override
+ public String getStatusDir() {
+ return statusDir;
+ }
+
+ @Override
+ public void setStatusDir(String statusDir) {
+ this.statusDir = statusDir;
+ }
+
+ @Override
+ public String getDataBase() {
+ return dataBase;
+ }
+
+ @Override
+ public void setDataBase(String dataBase) {
+ this.dataBase = dataBase;
+ }
+
+ @Override
+ public String getLogFile() {
+ return logFile;
+ }
+
+ @Override
+ public void setLogFile(String logFile) {
+ this.logFile = logFile;
+ }
+
+ @Override
+ public String getConfFile() {
+ return confFile;
+ }
+
+ @Override
+ public void setConfFile(String confFile) {
+ this.confFile = confFile;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobResourceManager.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobResourceManager.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobResourceManager.java
new file mode 100644
index 0000000..139b29a
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobResourceManager.java
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.client.*;
+import org.apache.ambari.view.hive.persistence.utils.FilteringStrategy;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.resources.PersonalCRUDResourceManager;
+import org.apache.ambari.view.hive.utils.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+/**
+ * Object that provides CRUD operations for query objects
+ */
+public class JobResourceManager extends PersonalCRUDResourceManager<Job> {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(JobResourceManager.class);
+
+ private JobControllerFactory jobControllerFactory;
+
+ /**
+ * Constructor
+ * @param context View Context instance
+ */
+ public JobResourceManager(ViewContext context) {
+ super(JobImpl.class, context);
+ jobControllerFactory = JobControllerFactory.getInstance(context);
+ }
+
+ @Override
+ public Job create(Job object) {
+ super.create(object);
+ JobController jobController = jobControllerFactory.createControllerForJob(object);
+
+ try {
+
+ jobController.afterCreation();
+ saveIfModified(jobController);
+
+ } catch (ServiceFormattedException e) {
+ cleanupAfterErrorAndThrowAgain(object, e);
+ }
+
+ return object;
+ }
+
+ private void saveIfModified(JobController jobController) {
+ if (jobController.isModified()) {
+ save(jobController.getJobPOJO());
+ jobController.clearModified();
+ }
+ }
+
+
+ @Override
+ public Job read(Integer id) throws ItemNotFound {
+ Job job = super.read(id);
+ JobController jobController = jobControllerFactory.createControllerForJob(job);
+ jobController.onRead();
+ saveIfModified(jobController);
+ return job;
+ }
+
+ @Override
+ public List<Job> readAll(FilteringStrategy filteringStrategy) {
+ return super.readAll(filteringStrategy);
+ }
+
+ @Override
+ public void delete(Integer resourceId) throws ItemNotFound {
+ super.delete(resourceId);
+ }
+
+ public JobController readController(Integer id) throws ItemNotFound {
+ Job job = read(id);
+ return jobControllerFactory.createControllerForJob(job);
+ }
+
+ public Cursor getJobResultsCursor(Job job) {
+ try {
+ JobController jobController = jobControllerFactory.createControllerForJob(job);
+ return jobController.getResults();
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException("Job results are expired", null);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobResourceProvider.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobResourceProvider.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobResourceProvider.java
new file mode 100644
index 0000000..780921d
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobResourceProvider.java
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import com.google.inject.Inject;
+import org.apache.ambari.view.*;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Resource provider for job
+ */
+public class JobResourceProvider implements ResourceProvider<Job> {
+ @Inject
+ ViewContext context;
+
+ protected JobResourceManager resourceManager = null;
+ protected final static Logger LOG =
+ LoggerFactory.getLogger(JobResourceProvider.class);
+
+ protected synchronized JobResourceManager getResourceManager() {
+ if (resourceManager == null) {
+ resourceManager = new JobResourceManager(context);
+ }
+ return resourceManager;
+ }
+
+ @Override
+ public Job getResource(String resourceId, Set<String> properties) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ try {
+ return getResourceManager().read(Integer.valueOf(resourceId));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NoSuchResourceException(resourceId);
+ }
+ }
+
+ @Override
+ public Set<Job> getResources(ReadRequest readRequest) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ return new HashSet<Job>(getResourceManager().readAll(
+ new OnlyOwnersFilteringStrategy(this.context.getUsername())));
+ }
+
+ @Override
+ public void createResource(String s, Map<String, Object> stringObjectMap) throws SystemException, ResourceAlreadyExistsException, NoSuchResourceException, UnsupportedPropertyException {
+ Job item = null;
+ try {
+ item = new JobImpl(stringObjectMap);
+ } catch (InvocationTargetException e) {
+ throw new SystemException("error on creating resource", e);
+ } catch (IllegalAccessException e) {
+ throw new SystemException("error on creating resource", e);
+ }
+ getResourceManager().create(item);
+ JobController jobController = JobControllerFactory.getInstance(context).createControllerForJob(item);
+ jobController.submit();
+ }
+
+ @Override
+ public boolean updateResource(String resourceId, Map<String, Object> stringObjectMap) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ Job item = null;
+ try {
+ item = new JobImpl(stringObjectMap);
+ } catch (InvocationTargetException e) {
+ throw new SystemException("error on updating resource", e);
+ } catch (IllegalAccessException e) {
+ throw new SystemException("error on updating resource", e);
+ }
+ try {
+ getResourceManager().update(item, Integer.valueOf(resourceId));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NoSuchResourceException(resourceId);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean deleteResource(String resourceId) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ try {
+ getResourceManager().delete(Integer.valueOf(resourceId));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NoSuchResourceException(resourceId);
+ }
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java
new file mode 100644
index 0000000..1c4f2a9
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/JobService.java
@@ -0,0 +1,355 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import com.google.inject.Inject;
+import org.apache.ambari.view.ViewResourceHandler;
+import org.apache.ambari.view.hive.BaseService;
+import org.apache.ambari.view.hive.backgroundjobs.BackgroundJobController;
+import org.apache.ambari.view.hive.client.Cursor;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
+import org.apache.ambari.view.hive.utils.*;
+import org.apache.ambari.view.hive.utils.HdfsApi;
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVPrinter;
+import org.apache.hadoop.fs.FSDataOutputStream;
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.*;
+import javax.ws.rs.core.*;
+import java.io.*;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+import java.util.concurrent.Callable;
+
+/**
+ * Servlet for queries
+ * API:
+ * GET /:id
+ * read job
+ * POST /
+ * create new job
+ * Required: title, queryFile
+ * GET /
+ * get all Jobs of current user
+ */
+public class JobService extends BaseService {
+ @Inject
+ ViewResourceHandler handler;
+
+ protected JobResourceManager resourceManager;
+ protected final static Logger LOG =
+ LoggerFactory.getLogger(JobService.class);
+
+ protected synchronized JobResourceManager getResourceManager() {
+ if (resourceManager == null) {
+ resourceManager = new JobResourceManager(context);
+ }
+ return resourceManager;
+ }
+
+ /**
+ * Get single item
+ */
+ @GET
+ @Path("{jobId}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getOne(@PathParam("jobId") String jobId) {
+ try {
+ JobController jobController = getResourceManager().readController(Integer.valueOf(jobId));
+
+ JSONObject jsonJob = jsonObjectFromJob(jobController);
+
+ return Response.ok(jsonJob).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ private JSONObject jsonObjectFromJob(JobController jobController) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
+ Map createdJobMap = PropertyUtils.describe(jobController.getJob());
+ createdJobMap.remove("class"); // no need to show Bean class on client
+
+ JSONObject jobJson = new JSONObject();
+ jobJson.put("job", createdJobMap);
+ return jobJson;
+ }
+
+ /**
+ * Get job results in csv format
+ */
+ @GET
+ @Path("{jobId}/results/csv")
+ @Produces("text/csv")
+ public Response getResultsCSV(@PathParam("jobId") String jobId,
+ @Context HttpServletResponse response,
+ @QueryParam("columns") final String requestedColumns) {
+ try {
+ JobController jobController = getResourceManager().readController(Integer.valueOf(jobId));
+ final Cursor resultSet = jobController.getResults();
+ resultSet.selectColumns(requestedColumns);
+
+ StreamingOutput stream = new StreamingOutput() {
+ @Override
+ public void write(OutputStream os) throws IOException, WebApplicationException {
+ Writer writer = new BufferedWriter(new OutputStreamWriter(os));
+ CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT);
+ try {
+ while (resultSet.hasNext()) {
+ csvPrinter.printRecord(resultSet.next().getRow());
+ writer.flush();
+ }
+ } finally {
+ writer.close();
+ }
+ }
+ };
+
+ return Response.ok(stream).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Get job results in csv format
+ */
+ @GET
+ @Path("{jobId}/results/csv/saveToHDFS")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getResultsToHDFS(@PathParam("jobId") String jobId,
+ @QueryParam("commence") String commence,
+ @QueryParam("file") final String targetFile,
+ @QueryParam("stop") final String stop,
+ @QueryParam("columns") final String requestedColumns,
+ @Context HttpServletResponse response) {
+ try {
+ final JobController jobController = getResourceManager().readController(Integer.valueOf(jobId));
+
+ String backgroundJobId = "csv" + String.valueOf(jobController.getJob().getId());
+ if (commence != null && commence.equals("true")) {
+ if (targetFile == null)
+ throw new MisconfigurationFormattedException("targetFile should not be empty");
+ BackgroundJobController.getInstance(context).startJob(String.valueOf(backgroundJobId), new Runnable() {
+ @Override
+ public void run() {
+
+ try {
+ Cursor resultSet = jobController.getResults();
+ resultSet.selectColumns(requestedColumns);
+
+ FSDataOutputStream stream = HdfsApi.getInstance(context).create(targetFile, true);
+ Writer writer = new BufferedWriter(new OutputStreamWriter(stream));
+ CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT);
+ try {
+ while (resultSet.hasNext() && !Thread.currentThread().isInterrupted()) {
+ csvPrinter.printRecord(resultSet.next().getRow());
+ writer.flush();
+ }
+ } finally {
+ writer.close();
+ }
+ stream.close();
+
+ } catch (IOException e) {
+ throw new ServiceFormattedException("Could not write CSV to HDFS for job#" + jobController.getJob().getId(), e);
+ } catch (InterruptedException e) {
+ throw new ServiceFormattedException("Could not write CSV to HDFS for job#" + jobController.getJob().getId(), e);
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException("Job results are expired", itemNotFound);
+ }
+
+ }
+ });
+ }
+
+ if (stop != null && stop.equals("true")) {
+ BackgroundJobController.getInstance(context).interrupt(backgroundJobId);
+ }
+
+ JSONObject object = new JSONObject();
+ object.put("stopped", BackgroundJobController.getInstance(context).isInterrupted(backgroundJobId));
+ object.put("jobId", jobController.getJob().getId());
+ object.put("backgroundJobId", backgroundJobId);
+ object.put("operationType", "CSV2HDFS");
+ object.put("status", BackgroundJobController.getInstance(context).state(backgroundJobId).toString());
+
+ return Response.ok(object).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Get next results page
+ */
+ @GET
+ @Path("{jobId}/results")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getResults(@PathParam("jobId") String jobId,
+ @QueryParam("first") String fromBeginning,
+ @QueryParam("count") Integer count,
+ @QueryParam("searchId") String searchId,
+ @QueryParam("columns") final String requestedColumns) {
+ try {
+ final JobController jobController = getResourceManager().readController(Integer.valueOf(jobId));
+
+ return ResultsPaginationController.getInstance(context)
+ .request(jobId, searchId, true, fromBeginning, count,
+ new Callable<Cursor>() {
+ @Override
+ public Cursor call() throws Exception {
+ Cursor cursor = jobController.getResults();
+ cursor.selectColumns(requestedColumns);
+ return cursor;
+ }
+ }).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Renew expiration time for results
+ */
+ @GET
+ @Path("{jobId}/results/keepAlive")
+ public Response keepAliveResults(@PathParam("jobId") String jobId,
+ @QueryParam("first") String fromBeginning,
+ @QueryParam("count") Integer count) {
+ try {
+ if (!ResultsPaginationController.getInstance(context).keepAlive(jobId, ResultsPaginationController.DEFAULT_SEARCH_ID)) {
+ throw new NotFoundFormattedException("Results already expired", null);
+ }
+ return Response.ok().build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Delete single item
+ */
+ @DELETE
+ @Path("{id}")
+ public Response delete(@PathParam("id") String id,
+ @QueryParam("remove") final String remove) {
+ try {
+ JobController jobController;
+ try {
+ jobController = getResourceManager().readController(Integer.valueOf(id));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ }
+ jobController.cancel();
+ if (remove != null && remove.compareTo("true") == 0) {
+ getResourceManager().delete(Integer.valueOf(id));
+ }
+// getResourceManager().delete(Integer.valueOf(queryId));
+ return Response.status(204).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Get all Jobs
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getList() {
+ try {
+ LOG.debug("Getting all job");
+ List allJobs = getResourceManager().readAll(
+ new OnlyOwnersFilteringStrategy(this.context.getUsername())); //TODO: move strategy to PersonalCRUDRM
+
+ JSONObject object = new JSONObject();
+ object.put("jobs", allJobs);
+ return Response.ok(object).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Create job
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response create(JobRequest request, @Context HttpServletResponse response,
+ @Context UriInfo ui) {
+ try {
+ Map jobInfo = PropertyUtils.describe(request.job);
+ Job job = new JobImpl(jobInfo);
+ getResourceManager().create(job);
+
+ JobController createdJobController = getResourceManager().readController(job.getId());
+ createdJobController.submit();
+
+ response.setHeader("Location",
+ String.format("%s/%s", ui.getAbsolutePath().toString(), job.getId()));
+
+ JSONObject jobObject = jsonObjectFromJob(createdJobController);
+
+ return Response.ok(jobObject).status(201).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Wrapper object for json mapping
+ */
+ public static class JobRequest {
+ public JobImpl job;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/LogParser.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/LogParser.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/LogParser.java
new file mode 100644
index 0000000..090781c
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/LogParser.java
@@ -0,0 +1,113 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import java.util.LinkedHashSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class LogParser {
+ public static final Pattern HADOOP_MR_JOBS_RE = Pattern.compile("(http[^\\s]*/proxy/([a-z0-9_]+?)/)");
+ public static final Pattern HADOOP_TEZ_JOBS_RE = Pattern.compile("\\(Executing on YARN cluster with App id ([a-z0-9_]+?)\\)");
+ private LinkedHashSet<JobId> jobsList;
+
+ public static LogParser parseLog(String logs) {
+ LogParser parser = new LogParser();
+
+ LinkedHashSet<JobId> mrJobIds = getMRJobIds(logs);
+ LinkedHashSet<JobId> tezJobIds = getTezJobIds(logs);
+
+ LinkedHashSet<JobId> jobIds = new LinkedHashSet<JobId>();
+ jobIds.addAll(mrJobIds);
+ jobIds.addAll(tezJobIds);
+
+ parser.setJobsList(jobIds);
+ return parser;
+ }
+
+ private static LinkedHashSet<JobId> getMRJobIds(String logs) {
+ Matcher m = HADOOP_MR_JOBS_RE.matcher(logs);
+ LinkedHashSet<JobId> list = new LinkedHashSet<JobId>();
+ while (m.find()) {
+ JobId applicationInfo = new JobId();
+ applicationInfo.setTrackingUrl(m.group(1));
+ applicationInfo.setIdentifier(m.group(2));
+ list.add(applicationInfo);
+ }
+ return list;
+ }
+
+ private static LinkedHashSet<JobId> getTezJobIds(String logs) {
+ Matcher m = HADOOP_TEZ_JOBS_RE.matcher(logs);
+ LinkedHashSet<JobId> list = new LinkedHashSet<JobId>();
+ while (m.find()) {
+ JobId applicationInfo = new JobId();
+ applicationInfo.setTrackingUrl(null);
+ applicationInfo.setIdentifier(m.group(1));
+ list.add(applicationInfo);
+ }
+ return list;
+ }
+
+ public void setJobsList(LinkedHashSet<JobId> jobsList) {
+ this.jobsList = jobsList;
+ }
+
+ public LinkedHashSet<JobId> getJobsList() {
+ return jobsList;
+ }
+
+ public static class JobId {
+ private String trackingUrl;
+ private String identifier;
+
+ public String getTrackingUrl() {
+ return trackingUrl;
+ }
+
+ public void setTrackingUrl(String trackingUrl) {
+ this.trackingUrl = trackingUrl;
+ }
+
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof JobId)) return false;
+
+ JobId jobId = (JobId) o;
+
+ if (!identifier.equals(jobId.identifier)) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return identifier.hashCode();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ModifyNotificationDelegate.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ModifyNotificationDelegate.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ModifyNotificationDelegate.java
new file mode 100644
index 0000000..8b48c97
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ModifyNotificationDelegate.java
@@ -0,0 +1,23 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+public interface ModifyNotificationDelegate {
+ boolean onModification(Object object);
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ModifyNotificationInvocationHandler.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ModifyNotificationInvocationHandler.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ModifyNotificationInvocationHandler.java
new file mode 100644
index 0000000..0552a8c
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ModifyNotificationInvocationHandler.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+public class ModifyNotificationInvocationHandler implements InvocationHandler {
+ private Object proxied;
+ private ModifyNotificationDelegate modifyDelegate;
+
+ public ModifyNotificationInvocationHandler(Object proxied, ModifyNotificationDelegate delegate) {
+ this.proxied = proxied;
+ this.modifyDelegate = delegate;
+ }
+
+ @Override
+ public Object invoke(Object o, Method method, Object[] args) throws Throwable {
+ if (method.getName().startsWith("set")) {
+ modifyDelegate.onModification(proxied);
+ }
+ return method.invoke(proxied, args);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java
new file mode 100644
index 0000000..1c1ce91
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/NoOperationStatusSetException.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+
+public class NoOperationStatusSetException extends Throwable {
+ public NoOperationStatusSetException(String s) {
+ super(s);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java
new file mode 100644
index 0000000..551ebdd
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleController.java
@@ -0,0 +1,113 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.client.ConnectionPool;
+import org.apache.ambari.view.hive.client.Cursor;
+import org.apache.ambari.view.hive.client.HiveClientException;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.apache.hive.service.cli.thrift.TGetOperationStatusResp;
+import org.apache.hive.service.cli.thrift.TOperationHandle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OperationHandleController {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(OperationHandleController.class);
+
+ private ViewContext context;
+ private TOperationHandle operationHandle;
+ private OperationHandleResourceManager operationHandlesStorage;
+
+ public OperationHandleController(ViewContext context, TOperationHandle storedOperationHandle, OperationHandleResourceManager operationHandlesStorage) {
+ this.context = context;
+ this.operationHandle = storedOperationHandle;
+ this.operationHandlesStorage = operationHandlesStorage;
+ }
+
+
+
+ public TOperationHandle getStoredOperationHandle() {
+ return operationHandle;
+ }
+
+ public void setStoredOperationHandle(TOperationHandle storedOperationHandle) {
+ this.operationHandle = storedOperationHandle;
+ }
+
+ public String getOperationStatus() throws NoOperationStatusSetException, HiveClientException {
+ TGetOperationStatusResp statusResp = ConnectionPool.getConnection(context).getOperationStatus(operationHandle);
+ if (!statusResp.isSetOperationState()) {
+ throw new NoOperationStatusSetException("Operation state is not set");
+ }
+
+ String status;
+ switch (statusResp.getOperationState()) {
+ case INITIALIZED_STATE:
+ status = Job.JOB_STATE_INITIALIZED;
+ break;
+ case RUNNING_STATE:
+ status = Job.JOB_STATE_RUNNING;
+ break;
+ case FINISHED_STATE:
+ status = Job.JOB_STATE_FINISHED;
+ break;
+ case CANCELED_STATE:
+ status = Job.JOB_STATE_CANCELED;
+ break;
+ case CLOSED_STATE:
+ status = Job.JOB_STATE_CLOSED;
+ break;
+ case ERROR_STATE:
+ status = Job.JOB_STATE_ERROR;
+ break;
+ case UKNOWN_STATE:
+ status = Job.JOB_STATE_UNKNOWN;
+ break;
+ case PENDING_STATE:
+ status = Job.JOB_STATE_PENDING;
+ break;
+ default:
+ throw new NoOperationStatusSetException("Unknown status " + statusResp.getOperationState());
+ }
+ return status;
+ }
+
+ public void cancel() {
+ try {
+ ConnectionPool.getConnection(context).cancelOperation(operationHandle);
+ } catch (HiveClientException e) {
+ throw new ServiceFormattedException("Cancel failed: " + e.toString(), e);
+ }
+ }
+
+ public void persistHandleForJob(Job job) {
+ operationHandlesStorage.putHandleForJob(operationHandle, job);
+ }
+
+ public String getLogs() {
+ return ConnectionPool.getConnection(context).getLogs(operationHandle);
+ }
+
+ public Cursor getResults() {
+ return ConnectionPool.getConnection(context).getResults(operationHandle);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleControllerFactory.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleControllerFactory.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleControllerFactory.java
new file mode 100644
index 0000000..5d4a8af
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleControllerFactory.java
@@ -0,0 +1,52 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.hive.service.cli.thrift.TOperationHandle;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class OperationHandleControllerFactory {
+ private ViewContext context;
+ private OperationHandleResourceManager operationHandlesStorage;
+
+ private OperationHandleControllerFactory(ViewContext context) {
+ this.context = context;
+ operationHandlesStorage = new OperationHandleResourceManager(context);
+ }
+
+ private static Map<String, OperationHandleControllerFactory> viewSingletonObjects = new HashMap<String, OperationHandleControllerFactory>();
+ public static OperationHandleControllerFactory getInstance(ViewContext context) {
+ if (!viewSingletonObjects.containsKey(context.getInstanceName()))
+ viewSingletonObjects.put(context.getInstanceName(), new OperationHandleControllerFactory(context));
+ return viewSingletonObjects.get(context.getInstanceName());
+ }
+
+ public OperationHandleController createControllerForHandle(TOperationHandle storedOperationHandle) {
+ return new OperationHandleController(context, storedOperationHandle, operationHandlesStorage);
+ }
+
+ public OperationHandleController getHandleForJob(Job job) throws ItemNotFound {
+ TOperationHandle handle = operationHandlesStorage.getHandleForJob(job);
+ return createControllerForHandle(handle);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java
new file mode 100644
index 0000000..cffed38
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/OperationHandleResourceManager.java
@@ -0,0 +1,85 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.utils.FilteringStrategy;
+import org.apache.ambari.view.hive.persistence.utils.Indexed;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.resources.SharedCRUDResourceManager;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.apache.hive.service.cli.thrift.TOperationHandle;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import java.util.List;
+
+public class OperationHandleResourceManager extends SharedCRUDResourceManager<StoredOperationHandle> {
+ /**
+ * Constructor
+ *
+ * @param context View Context instance
+ */
+ public OperationHandleResourceManager(ViewContext context) {
+ super(StoredOperationHandle.class, context);
+ }
+
+ public List<StoredOperationHandle> readJobRelatedHandles(final Job job) {
+ try {
+ return getStorage().loadWhere(StoredOperationHandle.class, "jobId = " + job.getId());
+ } catch (NotImplementedException e) {
+ // fallback to filtering strategy
+ return getStorage().loadAll(StoredOperationHandle.class, new FilteringStrategy() {
+ @Override
+ public boolean isConform(Indexed item) {
+ StoredOperationHandle handle = (StoredOperationHandle) item;
+ return (handle.getJobId() != null && handle.getJobId().equals(job.getId()));
+ }
+ });
+ }
+ }
+
+ public void putHandleForJob(TOperationHandle h, Job job) {
+ StoredOperationHandle handle = StoredOperationHandle.buildFromTOperationHandle(h);
+ handle.setJobId(job.getId());
+
+ List<StoredOperationHandle> jobRelatedHandles = readJobRelatedHandles(job);
+ if (jobRelatedHandles.size() > 0) {
+ handle.setId(jobRelatedHandles.get(0).getId()); // update existing
+ try {
+ update(handle, jobRelatedHandles.get(0).getId());
+ } catch (ItemNotFound itemNotFound) {
+ throw new ServiceFormattedException("Error when updating operation handle: " + itemNotFound.toString(), itemNotFound);
+ }
+ } else {
+ create(handle);
+ }
+ }
+
+ public boolean containsHandleForJob(Job job) {
+ List<StoredOperationHandle> jobRelatedHandles = readJobRelatedHandles(job);
+ return jobRelatedHandles.size() > 0;
+ }
+
+ public TOperationHandle getHandleForJob(Job job) throws ItemNotFound {
+ List<StoredOperationHandle> jobRelatedHandles = readJobRelatedHandles(job);
+ if (jobRelatedHandles.size() == 0)
+ throw new ItemNotFound();
+ return jobRelatedHandles.get(0).toTOperationHandle();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java
new file mode 100644
index 0000000..c152b03
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/ResultsPaginationController.java
@@ -0,0 +1,180 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.client.ColumnDescription;
+import org.apache.ambari.view.hive.client.HiveClientException;
+import org.apache.ambari.view.hive.client.Cursor;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.apache.commons.collections4.map.PassiveExpiringMap;
+
+import javax.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * Results Pagination Controller
+ * Persists cursors for result sets
+ */
+public class ResultsPaginationController {
+ public static final String DEFAULT_SEARCH_ID = "default";
+ private static Map<String, ResultsPaginationController> viewSingletonObjects = new HashMap<String, ResultsPaginationController>();
+ public static ResultsPaginationController getInstance(ViewContext context) {
+ if (!viewSingletonObjects.containsKey(context.getInstanceName()))
+ viewSingletonObjects.put(context.getInstanceName(), new ResultsPaginationController());
+ return viewSingletonObjects.get(context.getInstanceName());
+ }
+
+ public ResultsPaginationController() {
+ }
+
+ private static final long EXPIRING_TIME = 10*60*1000; // 10 minutes
+ private static final int DEFAULT_FETCH_COUNT = 50;
+ private Map<String, Cursor> resultsCache;
+
+ public static class CustomTimeToLiveExpirationPolicy extends PassiveExpiringMap.ConstantTimeToLiveExpirationPolicy<String, Cursor> {
+ public CustomTimeToLiveExpirationPolicy(long timeToLiveMillis) {
+ super(timeToLiveMillis);
+ }
+
+ @Override
+ public long expirationTime(String key, Cursor value) {
+ if (key.startsWith("$"))
+ return -1; //never expire
+ return super.expirationTime(key, value);
+ }
+ }
+
+ private Map<String, Cursor> getResultsCache() {
+ if (resultsCache == null) {
+ PassiveExpiringMap<String, Cursor> resultsCacheExpiringMap =
+ new PassiveExpiringMap<String, Cursor>(new CustomTimeToLiveExpirationPolicy(EXPIRING_TIME));
+ resultsCache = Collections.synchronizedMap(resultsCacheExpiringMap);
+ }
+ return resultsCache;
+ }
+
+ /**
+ * Renew timer of cache entry.
+ * @param key name/id of results request
+ * @return false if entry not found; true if renew was ok
+ */
+ public boolean keepAlive(String key, String searchId) {
+ if (searchId == null)
+ searchId = DEFAULT_SEARCH_ID;
+ String effectiveKey = key + "?" + searchId;
+ if (!getResultsCache().containsKey(effectiveKey))
+ return false;
+ Cursor cursor = getResultsCache().get(effectiveKey);
+ getResultsCache().put(effectiveKey, cursor);
+ return true;
+ }
+
+ private Cursor getResultsSet(String key, Callable<Cursor> makeResultsSet) {
+ if (!getResultsCache().containsKey(key)) {
+ Cursor resultSet = null;
+ try {
+ resultSet = makeResultsSet.call();
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ getResultsCache().put(key, resultSet);
+ }
+
+ return getResultsCache().get(key);
+ }
+
+ public Response.ResponseBuilder request(String key, String searchId, boolean canExpire, String fromBeginning, Integer count, Callable<Cursor> makeResultsSet) throws HiveClientException {
+ if (searchId == null)
+ searchId = DEFAULT_SEARCH_ID;
+ key = key + "?" + searchId;
+ if (!canExpire)
+ key = "$" + key;
+ if (fromBeginning != null && fromBeginning.equals("true") && getResultsCache().containsKey(key))
+ getResultsCache().remove(key);
+ Cursor resultSet = getResultsSet(key, makeResultsSet);
+
+ if (count == null)
+ count = DEFAULT_FETCH_COUNT;
+
+ ResultsResponse resultsResponse = new ResultsResponse();
+ resultsResponse.setSchema(resultSet.getSchema());
+ ArrayList<Object[]> rows = new ArrayList<Object[]>(count);
+ int read = resultSet.readRaw(rows, count);
+ resultsResponse.setRows(rows);
+ resultsResponse.setReadCount(read);
+ resultsResponse.setHasNext(resultSet.hasNext());
+// resultsResponse.setSize(resultSet.size());
+ resultsResponse.setOffset(resultSet.getOffset());
+ return Response.ok(resultsResponse);
+ }
+
+ private static class ResultsResponse {
+ private ArrayList<ColumnDescription> schema;
+ private ArrayList<Object[]> rows;
+ private int readCount;
+ private boolean hasNext;
+ private long offset;
+
+ public void setSchema(ArrayList<ColumnDescription> schema) {
+ this.schema = schema;
+ }
+
+ public ArrayList<ColumnDescription> getSchema() {
+ return schema;
+ }
+
+ public void setRows(ArrayList<Object[]> rows) {
+ this.rows = rows;
+ }
+
+ public ArrayList<Object[]> getRows() {
+ return rows;
+ }
+
+ public void setReadCount(int readCount) {
+ this.readCount = readCount;
+ }
+
+ public int getReadCount() {
+ return readCount;
+ }
+
+ public void setHasNext(boolean hasNext) {
+ this.hasNext = hasNext;
+ }
+
+ public boolean isHasNext() {
+ return hasNext;
+ }
+
+ public long getOffset() {
+ return offset;
+ }
+
+ public void setOffset(long offset) {
+ this.offset = offset;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java
new file mode 100644
index 0000000..193b226
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/jobs/StoredOperationHandle.java
@@ -0,0 +1,146 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.apache.ambari.view.hive.persistence.utils.Indexed;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.hive.service.cli.thrift.THandleIdentifier;
+import org.apache.hive.service.cli.thrift.TOperationHandle;
+import org.apache.hive.service.cli.thrift.TOperationType;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+
+/**
+ * Bean to represent TOperationHandle stored in DB
+ */
+public class StoredOperationHandle implements Indexed {
+ private boolean hasResultSet;
+ private double modifiedRowCount;
+ private int operationType;
+ private String guid;
+ private String secret;
+
+ private Integer jobId;
+
+ private Integer id;
+
+ public StoredOperationHandle() {}
+ public StoredOperationHandle(Map<String, Object> stringObjectMap) throws InvocationTargetException, IllegalAccessException {
+ for (Map.Entry<String, Object> entry : stringObjectMap.entrySet()) {
+ try {
+ PropertyUtils.setProperty(this, entry.getKey(), entry.getValue());
+ } catch (NoSuchMethodException e) {
+ //do nothing, skip
+ }
+ }
+ }
+
+
+ public static StoredOperationHandle buildFromTOperationHandle(TOperationHandle handle) {
+ StoredOperationHandle storedHandle = new StoredOperationHandle();
+ //bool hasResultSet
+ storedHandle.setHasResultSet(handle.isHasResultSet());
+ //optional double modifiedRowCount
+ storedHandle.setModifiedRowCount(handle.getModifiedRowCount());
+ //TOperationType operationType
+ storedHandle.setOperationType(handle.getOperationType().getValue());
+ //THandleIdentifier operationId
+ storedHandle.setGuid(Hex.encodeHexString(handle.getOperationId().getGuid()));
+ storedHandle.setSecret(Hex.encodeHexString(handle.getOperationId().getSecret()));
+ return storedHandle;
+ }
+
+ public TOperationHandle toTOperationHandle() {
+ TOperationHandle handle = new TOperationHandle();
+ handle.setHasResultSet(isHasResultSet());
+ handle.setModifiedRowCount(getModifiedRowCount());
+ handle.setOperationType(TOperationType.findByValue(getOperationType()));
+ THandleIdentifier identifier = new THandleIdentifier();
+ try {
+ identifier.setGuid(Hex.decodeHex(getGuid().toCharArray()));
+ identifier.setSecret(Hex.decodeHex(getSecret().toCharArray()));
+ } catch (DecoderException e) {
+ throw new ServiceFormattedException("Wrong identifer of OperationHandle is stored in DB");
+ }
+ handle.setOperationId(identifier);
+ return handle;
+ }
+
+ public boolean isHasResultSet() {
+ return hasResultSet;
+ }
+
+ public void setHasResultSet(boolean hasResultSet) {
+ this.hasResultSet = hasResultSet;
+ }
+
+ public double getModifiedRowCount() {
+ return modifiedRowCount;
+ }
+
+ public void setModifiedRowCount(double modifiedRowCount) {
+ this.modifiedRowCount = modifiedRowCount;
+ }
+
+ public int getOperationType() {
+ return operationType;
+ }
+
+ public void setOperationType(int operationType) {
+ this.operationType = operationType;
+ }
+
+ public String getGuid() {
+ return guid;
+ }
+
+ public void setGuid(String guid) {
+ this.guid = guid;
+ }
+
+ public String getSecret() {
+ return secret;
+ }
+
+ public void setSecret(String secret) {
+ this.secret = secret;
+ }
+
+ public Integer getJobId() {
+ return jobId;
+ }
+
+ public void setJobId(Integer jobId) {
+ this.jobId = jobId;
+ }
+
+ @Override
+ public Integer getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(Integer id) {
+ this.id = id;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceItem.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceItem.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceItem.java
new file mode 100644
index 0000000..ddd9990
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceItem.java
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.resources;
+
+import org.apache.ambari.view.hive.persistence.utils.PersonalResource;
+import org.apache.commons.beanutils.BeanUtils;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+
+/**
+ * Bean to represent file resource
+ */
+public class FileResourceItem implements Serializable, PersonalResource {
+ private String name;
+ private String path;
+
+ private Integer id;
+ private String owner;
+
+ public FileResourceItem() {}
+ public FileResourceItem(Map<String, Object> stringObjectMap) throws InvocationTargetException, IllegalAccessException {
+ BeanUtils.populate(this, stringObjectMap);
+ }
+
+ @Override
+ public Integer getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getOwner() {
+ return owner;
+ }
+
+ @Override
+ public void setOwner(String owner) {
+ this.owner = owner;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+}
[03/12] ambari git commit: AMBARI-9676. Initial Hive View Submission
to Contrib (alexantonenko)
Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/codemirror-min.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/codemirror-min.js b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/codemirror-min.js
new file mode 100644
index 0000000..a2bebeb
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/codemirror-min.js
@@ -0,0 +1,17 @@
+/* CodeMirror - Minified & Bundled
+ Generated on 10/30/2014 with http://codemirror.net/doc/compress.html
+ Version: HEAD
+
+ CodeMirror Library:
+ - codemirror.js
+ Modes:
+ - sql.js
+ Add-ons:
+ - sql-hint.js
+ */
+
+!function(a){if("object"==typeof exports&&"object"==typeof module)module.exports=a();else{if("function"==typeof define&&define.amd)return define([],a);this.CodeMirror=a()}}(function(){"use strict";function w(a,b){if(!(this instanceof w))return new w(a,b);this.options=b=b?Pg(b):{},Pg(ie,b,!1),J(b);var c=b.value;"string"==typeof c&&(c=new Kf(c,b.mode)),this.doc=c;var f=this.display=new x(a,c);f.wrapper.CodeMirror=this,F(this),D(this),b.lineWrapping&&(this.display.wrapper.className+=" CodeMirror-wrap"),b.autofocus&&!o&&_c(this),this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:!1,focused:!1,suppressEdits:!1,pasteIncoming:!1,cutIncoming:!1,draggingText:!1,highlight:new Fg,keySeq:null},d&&11>e&&setTimeout(Qg($c,this,!0),20),cd(this),hh(),Ac(this),this.curOp.forceUpdate=!0,Of(this,c),b.autofocus&&!o||ah()==f.input?setTimeout(Qg(Hd,this),20):Id(this);for(var g in je)je.hasOwnProperty(g)&&je[g](this,b[g],le);P(this);for(var h=0;h<pe.length;++h)pe[h](this);Cc(this)}function x(a,b){var c
=this,g=c.input=Xg("textarea",null,null,"position: absolute; padding: 0; width: 1px; height: 1em; outline: none");f?g.style.width="1000px":g.setAttribute("wrap","off"),n&&(g.style.border="1px solid black"),g.setAttribute("autocorrect","off"),g.setAttribute("autocapitalize","off"),g.setAttribute("spellcheck","false"),c.inputDiv=Xg("div",[g],null,"overflow: hidden; position: relative; width: 3px; height: 0px;"),c.scrollbarH=Xg("div",[Xg("div",null,null,"height: 100%; min-height: 1px")],"CodeMirror-hscrollbar"),c.scrollbarV=Xg("div",[Xg("div",null,null,"min-width: 1px")],"CodeMirror-vscrollbar"),c.scrollbarFiller=Xg("div",null,"CodeMirror-scrollbar-filler"),c.gutterFiller=Xg("div",null,"CodeMirror-gutter-filler"),c.lineDiv=Xg("div",null,"CodeMirror-code"),c.selectionDiv=Xg("div",null,null,"position: relative; z-index: 1"),c.cursorDiv=Xg("div",null,"CodeMirror-cursors"),c.measure=Xg("div",null,"CodeMirror-measure"),c.lineMeasure=Xg("div",null,"CodeMirror-measure"),c.lineSpace=Xg("div",[
c.measure,c.lineMeasure,c.selectionDiv,c.cursorDiv,c.lineDiv],null,"position: relative; outline: none"),c.mover=Xg("div",[Xg("div",[c.lineSpace],"CodeMirror-lines")],null,"position: relative"),c.sizer=Xg("div",[c.mover],"CodeMirror-sizer"),c.heightForcer=Xg("div",null,null,"position: absolute; height: "+Ag+"px; width: 1px;"),c.gutters=Xg("div",null,"CodeMirror-gutters"),c.lineGutter=null,c.scroller=Xg("div",[c.sizer,c.heightForcer,c.gutters],"CodeMirror-scroll"),c.scroller.setAttribute("tabIndex","-1"),c.wrapper=Xg("div",[c.inputDiv,c.scrollbarH,c.scrollbarV,c.scrollbarFiller,c.gutterFiller,c.scroller],"CodeMirror"),d&&8>e&&(c.gutters.style.zIndex=-1,c.scroller.style.paddingRight=0),n&&(g.style.width="0px"),f||(c.scroller.draggable=!0),k&&(c.inputDiv.style.height="1px",c.inputDiv.style.position="absolute"),d&&8>e&&(c.scrollbarH.style.minHeight=c.scrollbarV.style.minWidth="18px"),a&&(a.appendChild?a.appendChild(c.wrapper):a(c.wrapper)),c.viewFrom=c.viewTo=b.first,c.view=[],c.external
Measured=null,c.viewOffset=0,c.lastWrapHeight=c.lastWrapWidth=0,c.updateLineNumbers=null,c.lineNumWidth=c.lineNumInnerWidth=c.lineNumChars=null,c.prevInput="",c.alignWidgets=!1,c.pollingFast=!1,c.poll=new Fg,c.cachedCharWidth=c.cachedTextHeight=c.cachedPaddingH=null,c.inaccurateSelection=!1,c.maxLine=null,c.maxLineLength=0,c.maxLineChanged=!1,c.wheelDX=c.wheelDY=c.wheelStartX=c.wheelStartY=null,c.shift=!1,c.selForContextMenu=null}function y(a){a.doc.mode=w.getMode(a.options,a.doc.modeOption),z(a)}function z(a){a.doc.iter(function(a){a.stateAfter&&(a.stateAfter=null),a.styles&&(a.styles=null)}),a.doc.frontier=a.doc.first,Tb(a,100),a.state.modeGen++,a.curOp&&Pc(a)}function A(a){a.options.lineWrapping?(dh(a.display.wrapper,"CodeMirror-wrap"),a.display.sizer.style.minWidth=""):(ch(a.display.wrapper,"CodeMirror-wrap"),I(a)),C(a),Pc(a),kc(a),setTimeout(function(){M(a)},100)}function B(a){var b=wc(a.display),c=a.options.lineWrapping,d=c&&Math.max(5,a.display.scroller.clientWidth/xc(a.displ
ay)-3);return function(e){if(ef(a.doc,e))return 0;var f=0;if(e.widgets)for(var g=0;g<e.widgets.length;g++)e.widgets[g].height&&(f+=e.widgets[g].height);return c?f+(Math.ceil(e.text.length/d)||1)*b:f+b}}function C(a){var b=a.doc,c=B(a);b.iter(function(a){var b=c(a);b!=a.height&&Sf(a,b)})}function D(a){a.display.wrapper.className=a.display.wrapper.className.replace(/\s*cm-s-\S+/g,"")+a.options.theme.replace(/(^|\s)\s*/g," cm-s-"),kc(a)}function E(a){F(a),Pc(a),setTimeout(function(){O(a)},20)}function F(a){var b=a.display.gutters,c=a.options.gutters;Zg(b);for(var d=0;d<c.length;++d){var e=c[d],f=b.appendChild(Xg("div",null,"CodeMirror-gutter "+e));"CodeMirror-linenumbers"==e&&(a.display.lineGutter=f,f.style.width=(a.display.lineNumWidth||1)+"px")}b.style.display=d?"":"none",G(a)}function G(a){var b=a.display.gutters.offsetWidth;a.display.sizer.style.marginLeft=b+"px",a.display.scrollbarH.style.left=a.options.fixedGutter?b+"px":0}function H(a){if(0==a.height)return 0;for(var c,b=a.text.
length,d=a;c=Ze(d);){var e=c.find(0,!0);d=e.from.line,b+=e.from.ch-e.to.ch}for(d=a;c=$e(d);){var e=c.find(0,!0);b-=d.text.length-e.from.ch,d=e.to.line,b+=d.text.length-e.to.ch}return b}function I(a){var b=a.display,c=a.doc;b.maxLine=Pf(c,c.first),b.maxLineLength=H(b.maxLine),b.maxLineChanged=!0,c.iter(function(a){var c=H(a);c>b.maxLineLength&&(b.maxLineLength=c,b.maxLine=a)})}function J(a){var b=Mg(a.gutters,"CodeMirror-linenumbers");-1==b&&a.lineNumbers?a.gutters=a.gutters.concat(["CodeMirror-linenumbers"]):b>-1&&!a.lineNumbers&&(a.gutters=a.gutters.slice(0),a.gutters.splice(b,1))}function K(a){return a.display.scroller.clientHeight-a.display.wrapper.clientHeight<Ag-3}function L(a){var b=a.display.scroller;return{clientHeight:b.clientHeight,barHeight:a.display.scrollbarV.clientHeight,scrollWidth:b.scrollWidth,clientWidth:b.clientWidth,hScrollbarTakesSpace:K(a),barWidth:a.display.scrollbarH.clientWidth,docHeight:Math.round(a.doc.height+Yb(a.display))}}function M(a,b){b||(b=L(a));var
c=a.display,d=lh(c.measure),e=b.docHeight+Ag,f=b.scrollWidth>b.clientWidth;f&&b.scrollWidth<=b.clientWidth+1&&d>0&&!b.hScrollbarTakesSpace&&(f=!1);var g=e>b.clientHeight;if(g?(c.scrollbarV.style.display="block",c.scrollbarV.style.bottom=f?d+"px":"0",c.scrollbarV.firstChild.style.height=Math.max(0,e-b.clientHeight+(b.barHeight||c.scrollbarV.clientHeight))+"px"):(c.scrollbarV.style.display="",c.scrollbarV.firstChild.style.height="0"),f?(c.scrollbarH.style.display="block",c.scrollbarH.style.right=g?d+"px":"0",c.scrollbarH.firstChild.style.width=b.scrollWidth-b.clientWidth+(b.barWidth||c.scrollbarH.clientWidth)+"px"):(c.scrollbarH.style.display="",c.scrollbarH.firstChild.style.width="0"),f&&g?(c.scrollbarFiller.style.display="block",c.scrollbarFiller.style.height=c.scrollbarFiller.style.width=d+"px"):c.scrollbarFiller.style.display="",f&&a.options.coverGutterNextToScrollbar&&a.options.fixedGutter?(c.gutterFiller.style.display="block",c.gutterFiller.style.height=d+"px",c.gutterFiller.st
yle.width=c.gutters.offsetWidth+"px"):c.gutterFiller.style.display="",!a.state.checkedOverlayScrollbar&&b.clientHeight>0){if(0===d){var h=p&&!l?"12px":"18px";c.scrollbarV.style.minWidth=c.scrollbarH.style.minHeight=h;var i=function(b){og(b)!=c.scrollbarV&&og(b)!=c.scrollbarH&&Kc(a,gd)(b)};qg(c.scrollbarV,"mousedown",i),qg(c.scrollbarH,"mousedown",i)}a.state.checkedOverlayScrollbar=!0}}function N(a,b,c){var d=c&&null!=c.top?Math.max(0,c.top):a.scroller.scrollTop;d=Math.floor(d-Xb(a));var e=c&&null!=c.bottom?c.bottom:d+a.wrapper.clientHeight,f=Uf(b,d),g=Uf(b,e);if(c&&c.ensure){var h=c.ensure.from.line,i=c.ensure.to.line;if(f>h)return{from:h,to:Uf(b,Vf(Pf(b,h))+a.wrapper.clientHeight)};if(Math.min(i,b.lastLine())>=g)return{from:Uf(b,Vf(Pf(b,i))-a.wrapper.clientHeight),to:i}}return{from:f,to:Math.max(g,f+1)}}function O(a){var b=a.display,c=b.view;if(b.alignWidgets||b.gutters.firstChild&&a.options.fixedGutter){for(var d=R(b)-b.scroller.scrollLeft+a.doc.scrollLeft,e=b.gutters.offsetWidth,
f=d+"px",g=0;g<c.length;g++)if(!c[g].hidden){a.options.fixedGutter&&c[g].gutter&&(c[g].gutter.style.left=f);var h=c[g].alignable;if(h)for(var i=0;i<h.length;i++)h[i].style.left=f}a.options.fixedGutter&&(b.gutters.style.left=d+e+"px")}}function P(a){if(!a.options.lineNumbers)return!1;var b=a.doc,c=Q(a.options,b.first+b.size-1),d=a.display;if(c.length!=d.lineNumChars){var e=d.measure.appendChild(Xg("div",[Xg("div",c)],"CodeMirror-linenumber CodeMirror-gutter-elt")),f=e.firstChild.offsetWidth,g=e.offsetWidth-f;return d.lineGutter.style.width="",d.lineNumInnerWidth=Math.max(f,d.lineGutter.offsetWidth-g),d.lineNumWidth=d.lineNumInnerWidth+g,d.lineNumChars=d.lineNumInnerWidth?c.length:-1,d.lineGutter.style.width=d.lineNumWidth+"px",G(a),!0}return!1}function Q(a,b){return String(a.lineNumberFormatter(b+a.firstLineNumber))}function R(a){return a.scroller.getBoundingClientRect().left-a.sizer.getBoundingClientRect().left}function S(a,b,c){var d=a.display;this.viewport=b,this.visible=N(d,a.doc
,b),this.editorIsHidden=!d.wrapper.offsetWidth,this.wrapperHeight=d.wrapper.clientHeight,this.wrapperWidth=d.wrapper.clientWidth,this.oldViewFrom=d.viewFrom,this.oldViewTo=d.viewTo,this.oldScrollerWidth=d.scroller.clientWidth,this.force=c,this.dims=$(a)}function T(a,b){var c=a.display,d=a.doc;if(b.editorIsHidden)return Rc(a),!1;if(!b.force&&b.visible.from>=c.viewFrom&&b.visible.to<=c.viewTo&&(null==c.updateLineNumbers||c.updateLineNumbers>=c.viewTo)&&0==Vc(a))return!1;P(a)&&(Rc(a),b.dims=$(a));var e=d.first+d.size,f=Math.max(b.visible.from-a.options.viewportMargin,d.first),g=Math.min(e,b.visible.to+a.options.viewportMargin);c.viewFrom<f&&f-c.viewFrom<20&&(f=Math.max(d.first,c.viewFrom)),c.viewTo>g&&c.viewTo-g<20&&(g=Math.min(e,c.viewTo)),v&&(f=cf(a.doc,f),g=df(a.doc,g));var h=f!=c.viewFrom||g!=c.viewTo||c.lastWrapHeight!=b.wrapperHeight||c.lastWrapWidth!=b.wrapperWidth;Uc(a,f,g),c.viewOffset=Vf(Pf(a.doc,c.viewFrom)),a.display.mover.style.top=c.viewOffset+"px";var i=Vc(a);if(!h&&0==i
&&!b.force&&(null==c.updateLineNumbers||c.updateLineNumbers>=c.viewTo))return!1;var j=ah();return i>4&&(c.lineDiv.style.display="none"),_(a,c.updateLineNumbers,b.dims),i>4&&(c.lineDiv.style.display=""),j&&ah()!=j&&j.offsetHeight&&j.focus(),Zg(c.cursorDiv),Zg(c.selectionDiv),h&&(c.lastWrapHeight=b.wrapperHeight,c.lastWrapWidth=b.wrapperWidth,Tb(a,400)),c.updateLineNumbers=null,!0}function U(a,b){for(var c=b.force,d=b.viewport,e=!0;;e=!1){if(e&&a.options.lineWrapping&&b.oldScrollerWidth!=a.display.scroller.clientWidth)c=!0;else if(c=!1,d&&null!=d.top&&(d={top:Math.min(a.doc.height+Yb(a.display)-Ag-a.display.scroller.clientHeight,d.top)}),b.visible=N(a.display,a.doc,d),b.visible.from>=a.display.viewFrom&&b.visible.to<=a.display.viewTo)break;if(!T(a,b))break;Y(a);var f=L(a);Pb(a),W(a,f),M(a,f)}ug(a,"update",a),(a.display.viewFrom!=b.oldViewFrom||a.display.viewTo!=b.oldViewTo)&&ug(a,"viewportChange",a,a.display.viewFrom,a.display.viewTo)}function V(a,b){var c=new S(a,b);if(T(a,c)){Y(a),U
(a,c);var d=L(a);Pb(a),W(a,d),M(a,d)}}function W(a,b){a.display.sizer.style.minHeight=a.display.heightForcer.style.top=b.docHeight+"px",a.display.gutters.style.height=Math.max(b.docHeight,b.clientHeight-Ag)+"px"}function X(a,b){a.display.sizer.offsetWidth+a.display.gutters.offsetWidth<a.display.scroller.clientWidth-1&&(a.display.sizer.style.minHeight=a.display.heightForcer.style.top="0px",a.display.gutters.style.height=b.docHeight+"px")}function Y(a){for(var b=a.display,c=b.lineDiv.offsetTop,f=0;f<b.view.length;f++){var h,g=b.view[f];if(!g.hidden){if(d&&8>e){var i=g.node.offsetTop+g.node.offsetHeight;h=i-c,c=i}else{var j=g.node.getBoundingClientRect();h=j.bottom-j.top}var k=g.line.height-h;if(2>h&&(h=wc(b)),(k>.001||-.001>k)&&(Sf(g.line,h),Z(g.line),g.rest))for(var l=0;l<g.rest.length;l++)Z(g.rest[l])}}}function Z(a){if(a.widgets)for(var b=0;b<a.widgets.length;++b)a.widgets[b].height=a.widgets[b].node.offsetHeight}function $(a){for(var b=a.display,c={},d={},e=b.gutters.clientLeft,f=
b.gutters.firstChild,g=0;f;f=f.nextSibling,++g)c[a.options.gutters[g]]=f.offsetLeft+f.clientLeft+e,d[a.options.gutters[g]]=f.clientWidth;return{fixedPos:R(b),gutterTotalWidth:b.gutters.offsetWidth,gutterLeft:c,gutterWidth:d,wrapperWidth:b.wrapper.clientWidth}}function _(a,b,c){function i(b){var c=b.nextSibling;return f&&p&&a.display.currentWheelTarget==b?b.style.display="none":b.parentNode.removeChild(b),c}for(var d=a.display,e=a.options.lineNumbers,g=d.lineDiv,h=g.firstChild,j=d.view,k=d.viewFrom,l=0;l<j.length;l++){var m=j[l];if(m.hidden);else if(m.node){for(;h!=m.node;)h=i(h);var o=e&&null!=b&&k>=b&&m.lineNumber;m.changes&&(Mg(m.changes,"gutter")>-1&&(o=!1),ab(a,m,k,c)),o&&(Zg(m.lineNumber),m.lineNumber.appendChild(document.createTextNode(Q(a.options,k)))),h=m.node.nextSibling}else{var n=ib(a,m,k,c);g.insertBefore(n,h)}k+=m.size}for(;h;)h=i(h)}function ab(a,b,c,d){for(var e=0;e<b.changes.length;e++){var f=b.changes[e];"text"==f?eb(a,b):"gutter"==f?gb(a,b,c,d):"class"==f?fb(b):"wi
dget"==f&&hb(b,d)}b.changes=null}function bb(a){return a.node==a.text&&(a.node=Xg("div",null,null,"position: relative"),a.text.parentNode&&a.text.parentNode.replaceChild(a.node,a.text),a.node.appendChild(a.text),d&&8>e&&(a.node.style.zIndex=2)),a.node}function cb(a){var b=a.bgClass?a.bgClass+" "+(a.line.bgClass||""):a.line.bgClass;if(b&&(b+=" CodeMirror-linebackground"),a.background)b?a.background.className=b:(a.background.parentNode.removeChild(a.background),a.background=null);else if(b){var c=bb(a);a.background=c.insertBefore(Xg("div",null,b),c.firstChild)}}function db(a,b){var c=a.display.externalMeasured;return c&&c.line==b.line?(a.display.externalMeasured=null,b.measure=c.measure,c.built):yf(a,b)}function eb(a,b){var c=b.text.className,d=db(a,b);b.text==b.node&&(b.node=d.pre),b.text.parentNode.replaceChild(d.pre,b.text),b.text=d.pre,d.bgClass!=b.bgClass||d.textClass!=b.textClass?(b.bgClass=d.bgClass,b.textClass=d.textClass,fb(b)):c&&(b.text.className=c)}function fb(a){cb(a),a.l
ine.wrapClass?bb(a).className=a.line.wrapClass:a.node!=a.text&&(a.node.className="");var b=a.textClass?a.textClass+" "+(a.line.textClass||""):a.line.textClass;a.text.className=b||""}function gb(a,b,c,d){b.gutter&&(b.node.removeChild(b.gutter),b.gutter=null);var e=b.line.gutterMarkers;if(a.options.lineNumbers||e){var f=bb(b),g=b.gutter=f.insertBefore(Xg("div",null,"CodeMirror-gutter-wrapper","position: absolute; left: "+(a.options.fixedGutter?d.fixedPos:-d.gutterTotalWidth)+"px"),b.text);if(!a.options.lineNumbers||e&&e["CodeMirror-linenumbers"]||(b.lineNumber=g.appendChild(Xg("div",Q(a.options,c),"CodeMirror-linenumber CodeMirror-gutter-elt","left: "+d.gutterLeft["CodeMirror-linenumbers"]+"px; width: "+a.display.lineNumInnerWidth+"px"))),e)for(var h=0;h<a.options.gutters.length;++h){var i=a.options.gutters[h],j=e.hasOwnProperty(i)&&e[i];j&&g.appendChild(Xg("div",[j],"CodeMirror-gutter-elt","left: "+d.gutterLeft[i]+"px; width: "+d.gutterWidth[i]+"px"))}}}function hb(a,b){a.alignable&&
(a.alignable=null);for(var d,c=a.node.firstChild;c;c=d){var d=c.nextSibling;"CodeMirror-linewidget"==c.className&&a.node.removeChild(c)}jb(a,b)}function ib(a,b,c,d){var e=db(a,b);return b.text=b.node=e.pre,e.bgClass&&(b.bgClass=e.bgClass),e.textClass&&(b.textClass=e.textClass),fb(b),gb(a,b,c,d),jb(b,d),b.node}function jb(a,b){if(kb(a.line,a,b,!0),a.rest)for(var c=0;c<a.rest.length;c++)kb(a.rest[c],a,b,!1)}function kb(a,b,c,d){if(a.widgets)for(var e=bb(b),f=0,g=a.widgets;f<g.length;++f){var h=g[f],i=Xg("div",[h.node],"CodeMirror-linewidget");h.handleMouseEvents||(i.ignoreEvents=!0),lb(h,i,b,c),d&&h.above?e.insertBefore(i,b.gutter||b.text):e.appendChild(i),ug(h,"redraw")}}function lb(a,b,c,d){if(a.noHScroll){(c.alignable||(c.alignable=[])).push(b);var e=d.wrapperWidth;b.style.left=d.fixedPos+"px",a.coverGutter||(e-=d.gutterTotalWidth,b.style.paddingLeft=d.gutterTotalWidth+"px"),b.style.width=e+"px"}a.coverGutter&&(b.style.zIndex=5,b.style.position="relative",a.noHScroll||(b.style.marg
inLeft=-d.gutterTotalWidth+"px"))}function ob(a){return mb(a.line,a.ch)}function pb(a,b){return nb(a,b)<0?b:a}function qb(a,b){return nb(a,b)<0?a:b}function rb(a,b){this.ranges=a,this.primIndex=b}function sb(a,b){this.anchor=a,this.head=b}function tb(a,b){var c=a[b];a.sort(function(a,b){return nb(a.from(),b.from())}),b=Mg(a,c);for(var d=1;d<a.length;d++){var e=a[d],f=a[d-1];if(nb(f.to(),e.from())>=0){var g=qb(f.from(),e.from()),h=pb(f.to(),e.to()),i=f.empty()?e.from()==e.head:f.from()==f.head;b>=d&&--b,a.splice(--d,2,new sb(i?h:g,i?g:h))}}return new rb(a,b)}function ub(a,b){return new rb([new sb(a,b||a)],0)}function vb(a,b){return Math.max(a.first,Math.min(b,a.first+a.size-1))}function wb(a,b){if(b.line<a.first)return mb(a.first,0);var c=a.first+a.size-1;return b.line>c?mb(c,Pf(a,c).text.length):xb(b,Pf(a,b.line).text.length)}function xb(a,b){var c=a.ch;return null==c||c>b?mb(a.line,b):0>c?mb(a.line,0):a}function yb(a,b){return b>=a.first&&b<a.first+a.size}function zb(a,b){for(var c
=[],d=0;d<b.length;d++)c[d]=wb(a,b[d]);return c}function Ab(a,b,c,d){if(a.cm&&a.cm.display.shift||a.extend){var e=b.anchor;if(d){var f=nb(c,e)<0;f!=nb(d,e)<0?(e=c,c=d):f!=nb(c,d)<0&&(c=d)}return new sb(e,c)}return new sb(d||c,c)}function Bb(a,b,c,d){Hb(a,new rb([Ab(a,a.sel.primary(),b,c)],0),d)}function Cb(a,b,c){for(var d=[],e=0;e<a.sel.ranges.length;e++)d[e]=Ab(a,a.sel.ranges[e],b[e],null);var f=tb(d,a.sel.primIndex);Hb(a,f,c)}function Db(a,b,c,d){var e=a.sel.ranges.slice(0);e[b]=c,Hb(a,tb(e,a.sel.primIndex),d)}function Eb(a,b,c,d){Hb(a,ub(b,c),d)}function Fb(a,b){var c={ranges:b.ranges,update:function(b){this.ranges=[];for(var c=0;c<b.length;c++)this.ranges[c]=new sb(wb(a,b[c].anchor),wb(a,b[c].head))}};return sg(a,"beforeSelectionChange",a,c),a.cm&&sg(a.cm,"beforeSelectionChange",a.cm,c),c.ranges!=b.ranges?tb(c.ranges,c.ranges.length-1):b}function Gb(a,b,c){var d=a.history.done,e=Kg(d);e&&e.ranges?(d[d.length-1]=b,Ib(a,b,c)):Hb(a,b,c)}function Hb(a,b,c){Ib(a,b,c),bg(a,a.sel,a.cm
?a.cm.curOp.id:0/0,c)}function Ib(a,b,c){(yg(a,"beforeSelectionChange")||a.cm&&yg(a.cm,"beforeSelectionChange"))&&(b=Fb(a,b));var d=c&&c.bias||(nb(b.primary().head,a.sel.primary().head)<0?-1:1);Jb(a,Lb(a,b,d,!0)),c&&c.scroll===!1||!a.cm||be(a.cm)}function Jb(a,b){b.equals(a.sel)||(a.sel=b,a.cm&&(a.cm.curOp.updateInput=a.cm.curOp.selectionChanged=!0,xg(a.cm)),ug(a,"cursorActivity",a))}function Kb(a){Jb(a,Lb(a,a.sel,null,!1),Cg)}function Lb(a,b,c,d){for(var e,f=0;f<b.ranges.length;f++){var g=b.ranges[f],h=Mb(a,g.anchor,c,d),i=Mb(a,g.head,c,d);(e||h!=g.anchor||i!=g.head)&&(e||(e=b.ranges.slice(0,f)),e[f]=new sb(h,i))}return e?tb(e,b.primIndex):b}function Mb(a,b,c,d){var e=!1,f=b,g=c||1;a.cantEdit=!1;a:for(;;){var h=Pf(a,f.line);if(h.markedSpans)for(var i=0;i<h.markedSpans.length;++i){var j=h.markedSpans[i],k=j.marker;if((null==j.from||(k.inclusiveLeft?j.from<=f.ch:j.from<f.ch))&&(null==j.to||(k.inclusiveRight?j.to>=f.ch:j.to>f.ch))){if(d&&(sg(k,"beforeCursorEnter"),k.explicitlyCleared)
){if(h.markedSpans){--i;continue}break}if(!k.atomic)continue;var l=k.find(0>g?-1:1);if(0==nb(l,f)&&(l.ch+=g,l.ch<0?l=l.line>a.first?wb(a,mb(l.line-1)):null:l.ch>h.text.length&&(l=l.line<a.first+a.size-1?mb(l.line+1,0):null),!l)){if(e)return d?(a.cantEdit=!0,mb(a.first,0)):Mb(a,b,c,!0);e=!0,l=b,g=-g}f=l;continue a}}return f}}function Nb(a){for(var b=a.display,c=a.doc,d={},e=d.cursors=document.createDocumentFragment(),f=d.selection=document.createDocumentFragment(),g=0;g<c.sel.ranges.length;g++){var h=c.sel.ranges[g],i=h.empty();(i||a.options.showCursorWhenSelecting)&&Qb(a,h,e),i||Rb(a,h,f)}if(a.options.moveInputWithCursor){var j=qc(a,c.sel.primary().head,"div"),k=b.wrapper.getBoundingClientRect(),l=b.lineDiv.getBoundingClientRect();d.teTop=Math.max(0,Math.min(b.wrapper.clientHeight-10,j.top+l.top-k.top)),d.teLeft=Math.max(0,Math.min(b.wrapper.clientWidth-10,j.left+l.left-k.left))}return d}function Ob(a,b){$g(a.display.cursorDiv,b.cursors),$g(a.display.selectionDiv,b.selection),null!=
b.teTop&&(a.display.inputDiv.style.top=b.teTop+"px",a.display.inputDiv.style.left=b.teLeft+"px")}function Pb(a){Ob(a,Nb(a))}function Qb(a,b,c){var d=qc(a,b.head,"div",null,null,!a.options.singleCursorHeightPerLine),e=c.appendChild(Xg("div","\xa0","CodeMirror-cursor"));if(e.style.left=d.left+"px",e.style.top=d.top+"px",e.style.height=Math.max(0,d.bottom-d.top)*a.options.cursorHeight+"px",d.other){var f=c.appendChild(Xg("div","\xa0","CodeMirror-cursor CodeMirror-secondarycursor"));f.style.display="",f.style.left=d.other.left+"px",f.style.top=d.other.top+"px",f.style.height=.85*(d.other.bottom-d.other.top)+"px"}}function Rb(a,b,c){function j(a,b,c,d){0>b&&(b=0),b=Math.round(b),d=Math.round(d),f.appendChild(Xg("div",null,"CodeMirror-selected","position: absolute; left: "+a+"px; top: "+b+"px; width: "+(null==c?i-a:c)+"px; height: "+(d-b)+"px"))}function k(b,c,d){function m(c,d){return pc(a,mb(b,c),"div",f,d)}var k,l,f=Pf(e,b),g=f.text.length;return wh(Wf(f),c||0,null==d?g:d,function(a,b,
e){var n,o,p,f=m(a,"left");if(a==b)n=f,o=p=f.left;else{if(n=m(b-1,"right"),"rtl"==e){var q=f;f=n,n=q}o=f.left,p=n.right}null==c&&0==a&&(o=h),n.top-f.top>3&&(j(o,f.top,null,f.bottom),o=h,f.bottom<n.top&&j(o,f.bottom,null,n.top)),null==d&&b==g&&(p=i),(!k||f.top<k.top||f.top==k.top&&f.left<k.left)&&(k=f),(!l||n.bottom>l.bottom||n.bottom==l.bottom&&n.right>l.right)&&(l=n),h+1>o&&(o=h),j(o,n.top,p-o,n.bottom)}),{start:k,end:l}}var d=a.display,e=a.doc,f=document.createDocumentFragment(),g=Zb(a.display),h=g.left,i=d.lineSpace.offsetWidth-g.right,l=b.from(),m=b.to();if(l.line==m.line)k(l.line,l.ch,m.ch);else{var n=Pf(e,l.line),o=Pf(e,m.line),p=af(n)==af(o),q=k(l.line,l.ch,p?n.text.length+1:null).end,r=k(m.line,p?0:null,m.ch).start;p&&(q.top<r.top-2?(j(q.right,q.top,null,q.bottom),j(h,r.top,r.left,r.bottom)):j(q.right,q.top,r.left-q.right,q.bottom)),q.bottom<r.top&&j(h,q.bottom,null,r.top)}c.appendChild(f)}function Sb(a){if(a.state.focused){var b=a.display;clearInterval(b.blinker);var c=!0;b
.cursorDiv.style.visibility="",a.options.cursorBlinkRate>0?b.blinker=setInterval(function(){b.cursorDiv.style.visibility=(c=!c)?"":"hidden"},a.options.cursorBlinkRate):a.options.cursorBlinkRate<0&&(b.cursorDiv.style.visibility="hidden")}}function Tb(a,b){a.doc.mode.startState&&a.doc.frontier<a.display.viewTo&&a.state.highlight.set(b,Qg(Ub,a))}function Ub(a){var b=a.doc;if(b.frontier<b.first&&(b.frontier=b.first),!(b.frontier>=a.display.viewTo)){var c=+new Date+a.options.workTime,d=re(b.mode,Wb(a,b.frontier)),e=[];b.iter(b.frontier,Math.min(b.first+b.size,a.display.viewTo+500),function(f){if(b.frontier>=a.display.viewFrom){var g=f.styles,h=sf(a,f,d,!0);f.styles=h.styles;var i=f.styleClasses,j=h.classes;j?f.styleClasses=j:i&&(f.styleClasses=null);for(var k=!g||g.length!=f.styles.length||i!=j&&(!i||!j||i.bgClass!=j.bgClass||i.textClass!=j.textClass),l=0;!k&&l<g.length;++l)k=g[l]!=f.styles[l];k&&e.push(b.frontier),f.stateAfter=re(b.mode,d)}else uf(a,f.text,d),f.stateAfter=0==b.frontier%
5?re(b.mode,d):null;return++b.frontier,+new Date>c?(Tb(a,a.options.workDelay),!0):void 0}),e.length&&Jc(a,function(){for(var b=0;b<e.length;b++)Qc(a,e[b],"text")})}}function Vb(a,b,c){for(var d,e,f=a.doc,g=c?-1:b-(a.doc.mode.innerMode?1e3:100),h=b;h>g;--h){if(h<=f.first)return f.first;var i=Pf(f,h-1);if(i.stateAfter&&(!c||h<=f.frontier))return h;var j=Gg(i.text,null,a.options.tabSize);(null==e||d>j)&&(e=h-1,d=j)}return e}function Wb(a,b,c){var d=a.doc,e=a.display;if(!d.mode.startState)return!0;var f=Vb(a,b,c),g=f>d.first&&Pf(d,f-1).stateAfter;return g=g?re(d.mode,g):se(d.mode),d.iter(f,b,function(c){uf(a,c.text,g);var h=f==b-1||0==f%5||f>=e.viewFrom&&f<e.viewTo;c.stateAfter=h?re(d.mode,g):null,++f}),c&&(d.frontier=f),g}function Xb(a){return a.lineSpace.offsetTop}function Yb(a){return a.mover.offsetHeight-a.lineSpace.offsetHeight}function Zb(a){if(a.cachedPaddingH)return a.cachedPaddingH;var b=$g(a.measure,Xg("pre","x")),c=window.getComputedStyle?window.getComputedStyle(b):b.currentS
tyle,d={left:parseInt(c.paddingLeft),right:parseInt(c.paddingRight)};return isNaN(d.left)||isNaN(d.right)||(a.cachedPaddingH=d),d}function $b(a,b,c){var d=a.options.lineWrapping,e=d&&a.display.scroller.clientWidth;if(!b.measure.heights||d&&b.measure.width!=e){var f=b.measure.heights=[];if(d){b.measure.width=e;for(var g=b.text.firstChild.getClientRects(),h=0;h<g.length-1;h++){var i=g[h],j=g[h+1];Math.abs(i.bottom-j.bottom)>2&&f.push((i.bottom+j.top)/2-c.top)}}f.push(c.bottom-c.top)}}function _b(a,b,c){if(a.line==b)return{map:a.measure.map,cache:a.measure.cache};for(var d=0;d<a.rest.length;d++)if(a.rest[d]==b)return{map:a.measure.maps[d],cache:a.measure.caches[d]};for(var d=0;d<a.rest.length;d++)if(Tf(a.rest[d])>c)return{map:a.measure.maps[d],cache:a.measure.caches[d],before:!0}}function ac(a,b){b=af(b);var c=Tf(b),d=a.display.externalMeasured=new Nc(a.doc,b,c);d.lineN=c;var e=d.built=yf(a,d);return d.text=e.pre,$g(a.display.lineMeasure,e.pre),d}function bc(a,b,c,d){return ec(a,dc(a,b
),c,d)}function cc(a,b){if(b>=a.display.viewFrom&&b<a.display.viewTo)return a.display.view[Sc(a,b)];var c=a.display.externalMeasured;return c&&b>=c.lineN&&b<c.lineN+c.size?c:void 0}function dc(a,b){var c=Tf(b),d=cc(a,c);d&&!d.text?d=null:d&&d.changes&&ab(a,d,c,$(a)),d||(d=ac(a,b));var e=_b(d,b,c);return{line:b,view:d,rect:null,map:e.map,cache:e.cache,before:e.before,hasHeights:!1}}function ec(a,b,c,d,e){b.before&&(c=-1);var g,f=c+(d||"");return b.cache.hasOwnProperty(f)?g=b.cache[f]:(b.rect||(b.rect=b.view.text.getBoundingClientRect()),b.hasHeights||($b(a,b.view,b.rect),b.hasHeights=!0),g=gc(a,b,c,d),g.bogus||(b.cache[f]=g)),{left:g.left,right:g.right,top:e?g.rtop:g.top,bottom:e?g.rbottom:g.bottom}}function gc(a,b,c,f){for(var h,i,j,k,g=b.map,l=0;l<g.length;l+=3){var m=g[l],n=g[l+1];if(m>c?(i=0,j=1,k="left"):n>c?(i=c-m,j=i+1):(l==g.length-3||c==n&&g[l+3]>c)&&(j=n-m,i=j-1,c>=n&&(k="right")),null!=i){if(h=g[l+2],m==n&&f==(h.insertLeft?"left":"right")&&(k=f),"left"==f&&0==i)for(;l&&g[l
-2]==g[l-3]&&g[l-1].insertLeft;)h=g[(l-=3)+2],k="left";if("right"==f&&i==n-m)for(;l<g.length-3&&g[l+3]==g[l+4]&&!g[l+5].insertLeft;)h=g[(l+=3)+2],k="right";break}}var o;if(3==h.nodeType){for(var l=0;4>l;l++){for(;i&&Wg(b.line.text.charAt(m+i));)--i;for(;n>m+j&&Wg(b.line.text.charAt(m+j));)++j;if(d&&9>e&&0==i&&j==n-m)o=h.parentNode.getBoundingClientRect();else if(d&&a.options.lineWrapping){var p=Yg(h,i,j).getClientRects();o=p.length?p["right"==f?p.length-1:0]:fc}else o=Yg(h,i,j).getBoundingClientRect()||fc;if(o.left||o.right||0==i)break;j=i,i-=1,k="right"}d&&11>e&&(o=hc(a.display.measure,o))}else{i>0&&(k=f="right");var p;o=a.options.lineWrapping&&(p=h.getClientRects()).length>1?p["right"==f?p.length-1:0]:h.getBoundingClientRect()}if(d&&9>e&&!i&&(!o||!o.left&&!o.right)){var q=h.parentNode.getClientRects()[0];o=q?{left:q.left,right:q.left+xc(a.display),top:q.top,bottom:q.bottom}:fc}for(var r=o.top-b.rect.top,s=o.bottom-b.rect.top,t=(r+s)/2,u=b.view.measure.heights,l=0;l<u.length-1&&!(t
<u[l]);l++);var v=l?u[l-1]:0,w=u[l],x={left:("right"==k?o.right:o.left)-b.rect.left,right:("left"==k?o.left:o.right)-b.rect.left,top:v,bottom:w};return o.left||o.right||(x.bogus=!0),a.options.singleCursorHeightPerLine||(x.rtop=r,x.rbottom=s),x}function hc(a,b){if(!window.screen||null==screen.logicalXDPI||screen.logicalXDPI==screen.deviceXDPI||!uh(a))return b;var c=screen.logicalXDPI/screen.deviceXDPI,d=screen.logicalYDPI/screen.deviceYDPI;return{left:b.left*c,right:b.right*c,top:b.top*d,bottom:b.bottom*d}}function ic(a){if(a.measure&&(a.measure.cache={},a.measure.heights=null,a.rest))for(var b=0;b<a.rest.length;b++)a.measure.caches[b]={}}function jc(a){a.display.externalMeasure=null,Zg(a.display.lineMeasure);for(var b=0;b<a.display.view.length;b++)ic(a.display.view[b])}function kc(a){jc(a),a.display.cachedCharWidth=a.display.cachedTextHeight=a.display.cachedPaddingH=null,a.options.lineWrapping||(a.display.maxLineChanged=!0),a.display.lineNumChars=null}function lc(){return window.pag
eXOffset||(document.documentElement||document.body).scrollLeft}function mc(){return window.pageYOffset||(document.documentElement||document.body).scrollTop}function nc(a,b,c,d){if(b.widgets)for(var e=0;e<b.widgets.length;++e)if(b.widgets[e].above){var f=jf(b.widgets[e]);c.top+=f,c.bottom+=f}if("line"==d)return c;d||(d="local");var g=Vf(b);if("local"==d?g+=Xb(a.display):g-=a.display.viewOffset,"page"==d||"window"==d){var h=a.display.lineSpace.getBoundingClientRect();g+=h.top+("window"==d?0:mc());var i=h.left+("window"==d?0:lc());c.left+=i,c.right+=i}return c.top+=g,c.bottom+=g,c}function oc(a,b,c){if("div"==c)return b;var d=b.left,e=b.top;if("page"==c)d-=lc(),e-=mc();else if("local"==c||!c){var f=a.display.sizer.getBoundingClientRect();d+=f.left,e+=f.top}var g=a.display.lineSpace.getBoundingClientRect();return{left:d-g.left,top:e-g.top}}function pc(a,b,c,d,e){return d||(d=Pf(a.doc,b.line)),nc(a,d,bc(a,d,b.ch,e),c)}function qc(a,b,c,d,e,f){function g(b,g){var h=ec(a,e,b,g?"right":"lef
t",f);return g?h.left=h.right:h.right=h.left,nc(a,d,h,c)}function h(a,b){var c=i[b],d=c.level%2;return a==xh(c)&&b&&c.level<i[b-1].level?(c=i[--b],a=yh(c)-(c.level%2?0:1),d=!0):a==yh(c)&&b<i.length-1&&c.level<i[b+1].level&&(c=i[++b],a=xh(c)-c.level%2,d=!1),d&&a==c.to&&a>c.from?g(a-1):g(a,d)}d=d||Pf(a.doc,b.line),e||(e=dc(a,d));var i=Wf(d),j=b.ch;if(!i)return g(j);var k=Gh(i,j),l=h(j,k);return null!=Fh&&(l.other=h(j,Fh)),l}function rc(a,b){var c=0,b=wb(a.doc,b);a.options.lineWrapping||(c=xc(a.display)*b.ch);var d=Pf(a.doc,b.line),e=Vf(d)+Xb(a.display);return{left:c,right:c,top:e,bottom:e+d.height}}function sc(a,b,c,d){var e=mb(a,b);return e.xRel=d,c&&(e.outside=!0),e}function tc(a,b,c){var d=a.doc;if(c+=a.display.viewOffset,0>c)return sc(d.first,0,!0,-1);var e=Uf(d,c),f=d.first+d.size-1;if(e>f)return sc(d.first+d.size-1,Pf(d,f).text.length,!0,1);0>b&&(b=0);for(var g=Pf(d,e);;){var h=uc(a,g,e,b,c),i=$e(g),j=i&&i.find(0,!0);if(!i||!(h.ch>j.from.ch||h.ch==j.from.ch&&h.xRel>0))return h;e
=Tf(g=j.to.line)}}function uc(a,b,c,d,e){function j(d){var e=qc(a,mb(c,d),"line",b,i);return g=!0,f>e.bottom?e.left-h:f<e.top?e.left+h:(g=!1,e.left)}var f=e-Vf(b),g=!1,h=2*a.display.wrapper.clientWidth,i=dc(a,b),k=Wf(b),l=b.text.length,m=zh(b),n=Ah(b),o=j(m),p=g,q=j(n),r=g;if(d>q)return sc(c,n,r,1);for(;;){if(k?n==m||n==Ih(b,m,1):1>=n-m){for(var s=o>d||q-d>=d-o?m:n,t=d-(s==m?o:q);Wg(b.text.charAt(s));)++s;var u=sc(c,s,s==m?p:r,-1>t?-1:t>1?1:0);return u}var v=Math.ceil(l/2),w=m+v;if(k){w=m;for(var x=0;v>x;++x)w=Ih(b,w,1)}var y=j(w);y>d?(n=w,q=y,(r=g)&&(q+=1e3),l=v):(m=w,o=y,p=g,l-=v)}}function wc(a){if(null!=a.cachedTextHeight)return a.cachedTextHeight;if(null==vc){vc=Xg("pre");for(var b=0;49>b;++b)vc.appendChild(document.createTextNode("x")),vc.appendChild(Xg("br"));vc.appendChild(document.createTextNode("x"))}$g(a.measure,vc);var c=vc.offsetHeight/50;return c>3&&(a.cachedTextHeight=c),Zg(a.measure),c||1}function xc(a){if(null!=a.cachedCharWidth)return a.cachedCharWidth;var b=Xg("sp
an","xxxxxxxxxx"),c=Xg("pre",[b]);$g(a.measure,c);var d=b.getBoundingClientRect(),e=(d.right-d.left)/10;return e>2&&(a.cachedCharWidth=e),e||10}function Ac(a){a.curOp={cm:a,viewChanged:!1,startHeight:a.doc.height,forceUpdate:!1,updateInput:null,typing:!1,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:!1,updateMaxLine:!1,scrollLeft:null,scrollTop:null,scrollToPos:null,id:++zc},yc?yc.ops.push(a.curOp):a.curOp.ownsGroup=yc={ops:[a.curOp],delayedCallbacks:[]}}function Bc(a){var b=a.delayedCallbacks,c=0;do{for(;c<b.length;c++)b[c]();for(var d=0;d<a.ops.length;d++){var e=a.ops[d];if(e.cursorActivityHandlers)for(;e.cursorActivityCalled<e.cursorActivityHandlers.length;)e.cursorActivityHandlers[e.cursorActivityCalled++](e.cm)}}while(c<b.length)}function Cc(a){var b=a.curOp,c=b.ownsGroup;if(c)try{Bc(c)}finally{yc=null;for(var d=0;d<c.ops.length;d++)c.ops[d].cm.curOp=null;Dc(c)}}function Dc(a){for(var b=a.ops,c=0;c<b.length;c++)Ec(b[c]);for(var c=0;c<b.leng
th;c++)Fc(b[c]);for(var c=0;c<b.length;c++)Gc(b[c]);for(var c=0;c<b.length;c++)Hc(b[c]);for(var c=0;c<b.length;c++)Ic(b[c])
+}function Ec(a){var b=a.cm,c=b.display;a.updateMaxLine&&I(b),a.mustUpdate=a.viewChanged||a.forceUpdate||null!=a.scrollTop||a.scrollToPos&&(a.scrollToPos.from.line<c.viewFrom||a.scrollToPos.to.line>=c.viewTo)||c.maxLineChanged&&b.options.lineWrapping,a.update=a.mustUpdate&&new S(b,a.mustUpdate&&{top:a.scrollTop,ensure:a.scrollToPos},a.forceUpdate)}function Fc(a){a.updatedDisplay=a.mustUpdate&&T(a.cm,a.update)}function Gc(a){var b=a.cm,c=b.display;a.updatedDisplay&&Y(b),a.barMeasure=L(b),c.maxLineChanged&&!b.options.lineWrapping&&(a.adjustWidthTo=bc(b,c.maxLine,c.maxLine.text.length).left+3,a.maxScrollLeft=Math.max(0,c.sizer.offsetLeft+a.adjustWidthTo+Ag-c.scroller.clientWidth)),(a.updatedDisplay||a.selectionChanged)&&(a.newSelectionNodes=Nb(b))}function Hc(a){var b=a.cm;null!=a.adjustWidthTo&&(b.display.sizer.style.minWidth=a.adjustWidthTo+"px",a.maxScrollLeft<b.doc.scrollLeft&&sd(b,Math.min(b.display.scroller.scrollLeft,a.maxScrollLeft),!0),b.display.maxLineChanged=!1),a.newSelectio
nNodes&&Ob(b,a.newSelectionNodes),a.updatedDisplay&&W(b,a.barMeasure),(a.updatedDisplay||a.startHeight!=b.doc.height)&&M(b,a.barMeasure),a.selectionChanged&&Sb(b),b.state.focused&&a.updateInput&&$c(b,a.typing)}function Ic(a){var b=a.cm,c=b.display,d=b.doc;if(null!=a.adjustWidthTo&&Math.abs(a.barMeasure.scrollWidth-b.display.scroller.scrollWidth)>1&&M(b),a.updatedDisplay&&U(b,a.update),null==c.wheelStartX||null==a.scrollTop&&null==a.scrollLeft&&!a.scrollToPos||(c.wheelStartX=c.wheelStartY=null),null!=a.scrollTop&&(c.scroller.scrollTop!=a.scrollTop||a.forceScroll)){var e=Math.max(0,Math.min(c.scroller.scrollHeight-c.scroller.clientHeight,a.scrollTop));c.scroller.scrollTop=c.scrollbarV.scrollTop=d.scrollTop=e}if(null!=a.scrollLeft&&(c.scroller.scrollLeft!=a.scrollLeft||a.forceScroll)){var g=Math.max(0,Math.min(c.scroller.scrollWidth-c.scroller.clientWidth,a.scrollLeft));c.scroller.scrollLeft=c.scrollbarH.scrollLeft=d.scrollLeft=g,O(b)}if(a.scrollToPos){var h=Zd(b,wb(d,a.scrollToPos.fro
m),wb(d,a.scrollToPos.to),a.scrollToPos.margin);a.scrollToPos.isCursor&&b.state.focused&&Yd(b,h)}var i=a.maybeHiddenMarkers,j=a.maybeUnhiddenMarkers;if(i)for(var k=0;k<i.length;++k)i[k].lines.length||sg(i[k],"hide");if(j)for(var k=0;k<j.length;++k)j[k].lines.length&&sg(j[k],"unhide");c.wrapper.offsetHeight&&(d.scrollTop=b.display.scroller.scrollTop),a.updatedDisplay&&f&&(b.options.lineWrapping&&X(b,a.barMeasure),a.barMeasure.scrollWidth>a.barMeasure.clientWidth&&a.barMeasure.scrollWidth<a.barMeasure.clientWidth+1&&!K(b)&&M(b)),a.changeObjs&&sg(b,"changes",b,a.changeObjs)}function Jc(a,b){if(a.curOp)return b();Ac(a);try{return b()}finally{Cc(a)}}function Kc(a,b){return function(){if(a.curOp)return b.apply(a,arguments);Ac(a);try{return b.apply(a,arguments)}finally{Cc(a)}}}function Lc(a){return function(){if(this.curOp)return a.apply(this,arguments);Ac(this);try{return a.apply(this,arguments)}finally{Cc(this)}}}function Mc(a){return function(){var b=this.cm;if(!b||b.curOp)return a.appl
y(this,arguments);Ac(b);try{return a.apply(this,arguments)}finally{Cc(b)}}}function Nc(a,b,c){this.line=b,this.rest=bf(b),this.size=this.rest?Tf(Kg(this.rest))-c+1:1,this.node=this.text=null,this.hidden=ef(a,b)}function Oc(a,b,c){for(var e,d=[],f=b;c>f;f=e){var g=new Nc(a.doc,Pf(a.doc,f),f);e=f+g.size,d.push(g)}return d}function Pc(a,b,c,d){null==b&&(b=a.doc.first),null==c&&(c=a.doc.first+a.doc.size),d||(d=0);var e=a.display;if(d&&c<e.viewTo&&(null==e.updateLineNumbers||e.updateLineNumbers>b)&&(e.updateLineNumbers=b),a.curOp.viewChanged=!0,b>=e.viewTo)v&&cf(a.doc,b)<e.viewTo&&Rc(a);else if(c<=e.viewFrom)v&&df(a.doc,c+d)>e.viewFrom?Rc(a):(e.viewFrom+=d,e.viewTo+=d);else if(b<=e.viewFrom&&c>=e.viewTo)Rc(a);else if(b<=e.viewFrom){var f=Tc(a,c,c+d,1);f?(e.view=e.view.slice(f.index),e.viewFrom=f.lineN,e.viewTo+=d):Rc(a)}else if(c>=e.viewTo){var f=Tc(a,b,b,-1);f?(e.view=e.view.slice(0,f.index),e.viewTo=f.lineN):Rc(a)}else{var g=Tc(a,b,b,-1),h=Tc(a,c,c+d,1);g&&h?(e.view=e.view.slice(0,g.in
dex).concat(Oc(a,g.lineN,h.lineN)).concat(e.view.slice(h.index)),e.viewTo+=d):Rc(a)}var i=e.externalMeasured;i&&(c<i.lineN?i.lineN+=d:b<i.lineN+i.size&&(e.externalMeasured=null))}function Qc(a,b,c){a.curOp.viewChanged=!0;var d=a.display,e=a.display.externalMeasured;if(e&&b>=e.lineN&&b<e.lineN+e.size&&(d.externalMeasured=null),!(b<d.viewFrom||b>=d.viewTo)){var f=d.view[Sc(a,b)];if(null!=f.node){var g=f.changes||(f.changes=[]);-1==Mg(g,c)&&g.push(c)}}}function Rc(a){a.display.viewFrom=a.display.viewTo=a.doc.first,a.display.view=[],a.display.viewOffset=0}function Sc(a,b){if(b>=a.display.viewTo)return null;if(b-=a.display.viewFrom,0>b)return null;for(var c=a.display.view,d=0;d<c.length;d++)if(b-=c[d].size,0>b)return d}function Tc(a,b,c,d){var f,e=Sc(a,b),g=a.display.view;if(!v||c==a.doc.first+a.doc.size)return{index:e,lineN:c};for(var h=0,i=a.display.viewFrom;e>h;h++)i+=g[h].size;if(i!=b){if(d>0){if(e==g.length-1)return null;f=i+g[e].size-b,e++}else f=i-b;b+=f,c+=f}for(;cf(a.doc,c)!=c;)
{if(e==(0>d?0:g.length-1))return null;c+=d*g[e-(0>d?1:0)].size,e+=d}return{index:e,lineN:c}}function Uc(a,b,c){var d=a.display,e=d.view;0==e.length||b>=d.viewTo||c<=d.viewFrom?(d.view=Oc(a,b,c),d.viewFrom=b):(d.viewFrom>b?d.view=Oc(a,b,d.viewFrom).concat(d.view):d.viewFrom<b&&(d.view=d.view.slice(Sc(a,b))),d.viewFrom=b,d.viewTo<c?d.view=d.view.concat(Oc(a,d.viewTo,c)):d.viewTo>c&&(d.view=d.view.slice(0,Sc(a,c)))),d.viewTo=c}function Vc(a){for(var b=a.display.view,c=0,d=0;d<b.length;d++){var e=b[d];e.hidden||e.node&&!e.changes||++c}return c}function Wc(a){a.display.pollingFast||a.display.poll.set(a.options.pollInterval,function(){Zc(a),a.state.focused&&Wc(a)})}function Xc(a){function c(){var d=Zc(a);d||b?(a.display.pollingFast=!1,Wc(a)):(b=!0,a.display.poll.set(60,c))}var b=!1;a.display.pollingFast=!0,a.display.poll.set(20,c)}function Zc(a){var b=a.display.input,c=a.display.prevInput,f=a.doc;if(!a.state.focused||rh(b)&&!c||bd(a)||a.options.disableInput||a.state.keySeq)return!1;a.stat
e.pasteIncoming&&a.state.fakedLastChar&&(b.value=b.value.substring(0,b.value.length-1),a.state.fakedLastChar=!1);var g=b.value;if(g==c&&!a.somethingSelected())return!1;if(d&&e>=9&&a.display.inputHasSelection===g||p&&/[\uf700-\uf7ff]/.test(g))return $c(a),!1;var h=!a.curOp;h&&Ac(a),a.display.shift=!1,8203!=g.charCodeAt(0)||f.sel!=a.display.selForContextMenu||c||(c="\u200b");for(var i=0,j=Math.min(c.length,g.length);j>i&&c.charCodeAt(i)==g.charCodeAt(i);)++i;var k=g.slice(i),l=qh(k),m=null;a.state.pasteIncoming&&f.sel.ranges.length>1&&(Yc&&Yc.join("\n")==k?m=0==f.sel.ranges.length%Yc.length&&Ng(Yc,qh):l.length==f.sel.ranges.length&&(m=Ng(l,function(a){return[a]})));for(var n=f.sel.ranges.length-1;n>=0;n--){var o=f.sel.ranges[n],q=o.from(),r=o.to();i<c.length?q=mb(q.line,q.ch-(c.length-i)):a.state.overwrite&&o.empty()&&!a.state.pasteIncoming&&(r=mb(r.line,Math.min(Pf(f,r.line).text.length,r.ch+Kg(l).length)));var s=a.curOp.updateInput,t={from:q,to:r,text:m?m[n%m.length]:l,origin:a.stat
e.pasteIncoming?"paste":a.state.cutIncoming?"cut":"+input"};if(Rd(a.doc,t),ug(a,"inputRead",a,t),k&&!a.state.pasteIncoming&&a.options.electricChars&&a.options.smartIndent&&o.head.ch<100&&(!n||f.sel.ranges[n-1].head.line!=o.head.line)){var u=a.getModeAt(o.head),v=Ld(t);if(u.electricChars){for(var w=0;w<u.electricChars.length;w++)if(k.indexOf(u.electricChars.charAt(w))>-1){de(a,v.line,"smart");break}}else u.electricInput&&u.electricInput.test(Pf(f,v.line).text.slice(0,v.ch))&&de(a,v.line,"smart")}}return be(a),a.curOp.updateInput=s,a.curOp.typing=!0,g.length>1e3||g.indexOf("\n")>-1?b.value=a.display.prevInput="":a.display.prevInput=g,h&&Cc(a),a.state.pasteIncoming=a.state.cutIncoming=!1,!0}function $c(a,b){var c,f,g=a.doc;if(a.somethingSelected()){a.display.prevInput="";var h=g.sel.primary();c=sh&&(h.to().line-h.from().line>100||(f=a.getSelection()).length>1e3);var i=c?"-":f||a.getSelection();a.display.input.value=i,a.state.focused&&Lg(a.display.input),d&&e>=9&&(a.display.inputHasSele
ction=i)}else b||(a.display.prevInput=a.display.input.value="",d&&e>=9&&(a.display.inputHasSelection=null));a.display.inaccurateSelection=c}function _c(a){"nocursor"==a.options.readOnly||o&&ah()==a.display.input||a.display.input.focus()}function ad(a){a.state.focused||(_c(a),Hd(a))}function bd(a){return a.options.readOnly||a.doc.cantEdit}function cd(a){function c(){a.state.focused&&setTimeout(Qg(_c,a),0)}function g(b){wg(a,b)||ng(b)}function h(c){if(a.somethingSelected())Yc=a.getSelections(),b.inaccurateSelection&&(b.prevInput="",b.inaccurateSelection=!1,b.input.value=Yc.join("\n"),Lg(b.input));else{for(var d=[],e=[],f=0;f<a.doc.sel.ranges.length;f++){var g=a.doc.sel.ranges[f].head.line,h={anchor:mb(g,0),head:mb(g+1,0)};e.push(h),d.push(a.getRange(h.anchor,h.head))}"cut"==c.type?a.setSelections(e,null,Cg):(b.prevInput="",b.input.value=d.join("\n"),Lg(b.input)),Yc=d}"cut"==c.type&&(a.state.cutIncoming=!0)}var b=a.display;qg(b.scroller,"mousedown",Kc(a,gd)),d&&11>e?qg(b.scroller,"dblc
lick",Kc(a,function(b){if(!wg(a,b)){var c=fd(a,b);if(c&&!nd(a,b)&&!ed(a.display,b)){kg(b);var d=a.findWordAt(c);Bb(a.doc,d.anchor,d.head)}}})):qg(b.scroller,"dblclick",function(b){wg(a,b)||kg(b)}),qg(b.lineSpace,"selectstart",function(a){ed(b,a)||kg(a)}),t||qg(b.scroller,"contextmenu",function(b){Jd(a,b)}),qg(b.scroller,"scroll",function(){b.scroller.clientHeight&&(rd(a,b.scroller.scrollTop),sd(a,b.scroller.scrollLeft,!0),sg(a,"scroll",a))}),qg(b.scrollbarV,"scroll",function(){b.scroller.clientHeight&&rd(a,b.scrollbarV.scrollTop)}),qg(b.scrollbarH,"scroll",function(){b.scroller.clientHeight&&sd(a,b.scrollbarH.scrollLeft)}),qg(b.scroller,"mousewheel",function(b){vd(a,b)}),qg(b.scroller,"DOMMouseScroll",function(b){vd(a,b)}),qg(b.scrollbarH,"mousedown",c),qg(b.scrollbarV,"mousedown",c),qg(b.wrapper,"scroll",function(){b.wrapper.scrollTop=b.wrapper.scrollLeft=0}),qg(b.input,"keyup",function(b){Fd.call(a,b)}),qg(b.input,"input",function(){d&&e>=9&&a.display.inputHasSelection&&(a.display
.inputHasSelection=null),Xc(a)}),qg(b.input,"keydown",Kc(a,Dd)),qg(b.input,"keypress",Kc(a,Gd)),qg(b.input,"focus",Qg(Hd,a)),qg(b.input,"blur",Qg(Id,a)),a.options.dragDrop&&(qg(b.scroller,"dragstart",function(b){qd(a,b)}),qg(b.scroller,"dragenter",g),qg(b.scroller,"dragover",g),qg(b.scroller,"drop",Kc(a,pd))),qg(b.scroller,"paste",function(c){ed(b,c)||(a.state.pasteIncoming=!0,_c(a),Xc(a))}),qg(b.input,"paste",function(){if(f&&!a.state.fakedLastChar&&!(new Date-a.state.lastMiddleDown<200)){var c=b.input.selectionStart,d=b.input.selectionEnd;b.input.value+="$",b.input.selectionEnd=d,b.input.selectionStart=c,a.state.fakedLastChar=!0}a.state.pasteIncoming=!0,Xc(a)}),qg(b.input,"cut",h),qg(b.input,"copy",h),k&&qg(b.sizer,"mouseup",function(){ah()==b.input&&b.input.blur(),_c(a)})}function dd(a){var b=a.display;(b.lastWrapHeight!=b.wrapper.clientHeight||b.lastWrapWidth!=b.wrapper.clientWidth)&&(b.cachedCharWidth=b.cachedTextHeight=b.cachedPaddingH=null,a.setSize())}function ed(a,b){for(va
r c=og(b);c!=a.wrapper;c=c.parentNode)if(!c||c.ignoreEvents||c.parentNode==a.sizer&&c!=a.mover)return!0}function fd(a,b,c,d){var e=a.display;if(!c){var f=og(b);if(f==e.scrollbarH||f==e.scrollbarV||f==e.scrollbarFiller||f==e.gutterFiller)return null}var g,h,i=e.lineSpace.getBoundingClientRect();try{g=b.clientX-i.left,h=b.clientY-i.top}catch(b){return null}var k,j=tc(a,g,h);if(d&&1==j.xRel&&(k=Pf(a.doc,j.line).text).length==j.ch){var l=Gg(k,k.length,a.options.tabSize)-k.length;j=mb(j.line,Math.max(0,Math.round((g-Zb(a.display).left)/xc(a.display))-l))}return j}function gd(a){if(!wg(this,a)){var b=this,c=b.display;if(c.shift=a.shiftKey,ed(c,a))return f||(c.scroller.draggable=!1,setTimeout(function(){c.scroller.draggable=!0},100)),void 0;if(!nd(b,a)){var d=fd(b,a);switch(window.focus(),pg(a)){case 1:d?jd(b,a,d):og(a)==c.scroller&&kg(a);break;case 2:f&&(b.state.lastMiddleDown=+new Date),d&&Bb(b.doc,d),setTimeout(Qg(_c,b),20),kg(a);break;case 3:t&&Jd(b,a)}}}}function jd(a,b,c){setTimeout(
Qg(ad,a),0);var e,d=+new Date;id&&id.time>d-400&&0==nb(id.pos,c)?e="triple":hd&&hd.time>d-400&&0==nb(hd.pos,c)?(e="double",id={time:d,pos:c}):(e="single",hd={time:d,pos:c});var f=a.doc.sel,g=p?b.metaKey:b.ctrlKey;a.options.dragDrop&&jh&&!bd(a)&&"single"==e&&f.contains(c)>-1&&f.somethingSelected()?kd(a,b,c,g):ld(a,b,c,e,g)}function kd(a,b,c,g){var h=a.display,i=Kc(a,function(j){f&&(h.scroller.draggable=!1),a.state.draggingText=!1,rg(document,"mouseup",i),rg(h.scroller,"drop",i),Math.abs(b.clientX-j.clientX)+Math.abs(b.clientY-j.clientY)<10&&(kg(j),g||Bb(a.doc,c),_c(a),d&&9==e&&setTimeout(function(){document.body.focus(),_c(a)},20))});f&&(h.scroller.draggable=!0),a.state.draggingText=i,h.scroller.dragDrop&&h.scroller.dragDrop(),qg(document,"mouseup",i),qg(h.scroller,"drop",i)}function ld(a,b,c,d,e){function n(b){if(0!=nb(m,b))if(m=b,"rect"==d){for(var e=[],f=a.options.tabSize,k=Gg(Pf(g,c.line).text,c.ch,f),l=Gg(Pf(g,b.line).text,b.ch,f),n=Math.min(k,l),o=Math.max(k,l),p=Math.min(c.lin
e,b.line),q=Math.min(a.lastLine(),Math.max(c.line,b.line));q>=p;p++){var r=Pf(g,p).text,s=Hg(r,n,f);n==o?e.push(new sb(mb(p,s),mb(p,s))):r.length>s&&e.push(new sb(mb(p,s),mb(p,Hg(r,o,f))))}e.length||e.push(new sb(c,c)),Hb(g,tb(j.ranges.slice(0,i).concat(e),i),{origin:"*mouse",scroll:!1}),a.scrollIntoView(b)}else{var t=h,u=t.anchor,v=b;if("single"!=d){if("double"==d)var w=a.findWordAt(b);else var w=new sb(mb(b.line,0),wb(g,mb(b.line+1,0)));nb(w.anchor,u)>0?(v=w.head,u=qb(t.from(),w.anchor)):(v=w.anchor,u=pb(t.to(),w.head))}var e=j.ranges.slice(0);e[i]=new sb(wb(g,u),v),Hb(g,tb(e,i),Dg)}}function q(b){var c=++p,e=fd(a,b,!0,"rect"==d);if(e)if(0!=nb(e,m)){ad(a),n(e);var h=N(f,g);(e.line>=h.to||e.line<h.from)&&setTimeout(Kc(a,function(){p==c&&q(b)}),150)}else{var i=b.clientY<o.top?-20:b.clientY>o.bottom?20:0;i&&setTimeout(Kc(a,function(){p==c&&(f.scroller.scrollTop+=i,q(b))}),50)}}function r(b){p=1/0,kg(b),_c(a),rg(document,"mousemove",s),rg(document,"mouseup",t),g.history.lastSelOrigin=
null}var f=a.display,g=a.doc;kg(b);var h,i,j=g.sel;if(e&&!b.shiftKey?(i=g.sel.contains(c),h=i>-1?g.sel.ranges[i]:new sb(c,c)):h=g.sel.primary(),b.altKey)d="rect",e||(h=new sb(c,c)),c=fd(a,b,!0,!0),i=-1;else if("double"==d){var k=a.findWordAt(c);h=a.display.shift||g.extend?Ab(g,h,k.anchor,k.head):k}else if("triple"==d){var l=new sb(mb(c.line,0),wb(g,mb(c.line+1,0)));h=a.display.shift||g.extend?Ab(g,h,l.anchor,l.head):l}else h=Ab(g,h,c);e?i>-1?Db(g,i,h,Dg):(i=g.sel.ranges.length,Hb(g,tb(g.sel.ranges.concat([h]),i),{scroll:!1,origin:"*mouse"})):(i=0,Hb(g,new rb([h],0),Dg),j=g.sel);var m=c,o=f.wrapper.getBoundingClientRect(),p=0,s=Kc(a,function(a){pg(a)?q(a):r(a)}),t=Kc(a,r);qg(document,"mousemove",s),qg(document,"mouseup",t)}function md(a,b,c,d,e){try{var f=b.clientX,g=b.clientY}catch(b){return!1}if(f>=Math.floor(a.display.gutters.getBoundingClientRect().right))return!1;d&&kg(b);var h=a.display,i=h.lineDiv.getBoundingClientRect();if(g>i.bottom||!yg(a,c))return mg(b);g-=i.top-h.viewOffs
et;for(var j=0;j<a.options.gutters.length;++j){var k=h.gutters.childNodes[j];if(k&&k.getBoundingClientRect().right>=f){var l=Uf(a.doc,g),m=a.options.gutters[j];return e(a,c,a,l,m,b),mg(b)}}}function nd(a,b){return md(a,b,"gutterClick",!0,ug)}function pd(a){var b=this;if(!wg(b,a)&&!ed(b.display,a)){kg(a),d&&(od=+new Date);var c=fd(b,a,!0),e=a.dataTransfer.files;if(c&&!bd(b))if(e&&e.length&&window.FileReader&&window.File)for(var f=e.length,g=Array(f),h=0,i=function(a,d){var e=new FileReader;e.onload=Kc(b,function(){if(g[d]=e.result,++h==f){c=wb(b.doc,c);var a={from:c,to:c,text:qh(g.join("\n")),origin:"paste"};Rd(b.doc,a),Gb(b.doc,ub(c,Ld(a)))}}),e.readAsText(a)},j=0;f>j;++j)i(e[j],j);else{if(b.state.draggingText&&b.doc.sel.contains(c)>-1)return b.state.draggingText(a),setTimeout(Qg(_c,b),20),void 0;try{var g=a.dataTransfer.getData("Text");if(g){if(b.state.draggingText&&!(p?a.metaKey:a.ctrlKey))var k=b.listSelections();if(Ib(b.doc,ub(c,c)),k)for(var j=0;j<k.length;++j)Xd(b.doc,"",k[j].
anchor,k[j].head,"drag");b.replaceSelection(g,"around","paste"),_c(b)}}catch(a){}}}}function qd(a,b){if(d&&(!a.state.draggingText||+new Date-od<100))return ng(b),void 0;if(!wg(a,b)&&!ed(a.display,b)&&(b.dataTransfer.setData("Text",a.getSelection()),b.dataTransfer.setDragImage&&!j)){var c=Xg("img",null,null,"position: fixed; left: 0; top: 0;");c.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",i&&(c.width=c.height=1,a.display.wrapper.appendChild(c),c._top=c.offsetTop),b.dataTransfer.setDragImage(c,0,0),i&&c.parentNode.removeChild(c)}}function rd(b,c){Math.abs(b.doc.scrollTop-c)<2||(b.doc.scrollTop=c,a||V(b,{top:c}),b.display.scroller.scrollTop!=c&&(b.display.scroller.scrollTop=c),b.display.scrollbarV.scrollTop!=c&&(b.display.scrollbarV.scrollTop=c),a&&V(b),Tb(b,100))}function sd(a,b,c){(c?b==a.doc.scrollLeft:Math.abs(a.doc.scrollLeft-b)<2)||(b=Math.min(b,a.display.scroller.scrollWidth-a.display.scroller.clientWidth),a.doc.scrollLeft=b,O(a),a.display.sc
roller.scrollLeft!=b&&(a.display.scroller.scrollLeft=b),a.display.scrollbarH.scrollLeft!=b&&(a.display.scrollbarH.scrollLeft=b))}function vd(b,c){var d=c.wheelDeltaX,e=c.wheelDeltaY;null==d&&c.detail&&c.axis==c.HORIZONTAL_AXIS&&(d=c.detail),null==e&&c.detail&&c.axis==c.VERTICAL_AXIS?e=c.detail:null==e&&(e=c.wheelDelta);var g=b.display,h=g.scroller;if(d&&h.scrollWidth>h.clientWidth||e&&h.scrollHeight>h.clientHeight){if(e&&p&&f)a:for(var j=c.target,k=g.view;j!=h;j=j.parentNode)for(var l=0;l<k.length;l++)if(k[l].node==j){b.display.currentWheelTarget=j;break a}if(d&&!a&&!i&&null!=ud)return e&&rd(b,Math.max(0,Math.min(h.scrollTop+e*ud,h.scrollHeight-h.clientHeight))),sd(b,Math.max(0,Math.min(h.scrollLeft+d*ud,h.scrollWidth-h.clientWidth))),kg(c),g.wheelStartX=null,void 0;if(e&&null!=ud){var m=e*ud,n=b.doc.scrollTop,o=n+g.wrapper.clientHeight;0>m?n=Math.max(0,n+m-50):o=Math.min(b.doc.height,o+m+50),V(b,{top:n,bottom:o})}20>td&&(null==g.wheelStartX?(g.wheelStartX=h.scrollLeft,g.wheelStartY
=h.scrollTop,g.wheelDX=d,g.wheelDY=e,setTimeout(function(){if(null!=g.wheelStartX){var a=h.scrollLeft-g.wheelStartX,b=h.scrollTop-g.wheelStartY,c=b&&g.wheelDY&&b/g.wheelDY||a&&g.wheelDX&&a/g.wheelDX;g.wheelStartX=g.wheelStartY=null,c&&(ud=(ud*td+c)/(td+1),++td)}},200)):(g.wheelDX+=d,g.wheelDY+=e))}}function wd(a,b,c){if("string"==typeof b&&(b=te[b],!b))return!1;a.display.pollingFast&&Zc(a)&&(a.display.pollingFast=!1);var d=a.display.shift,e=!1;try{bd(a)&&(a.state.suppressEdits=!0),c&&(a.display.shift=!1),e=b(a)!=Bg}finally{a.display.shift=d,a.state.suppressEdits=!1}return e}function xd(a,b,c){for(var d=0;d<a.state.keyMaps.length;d++){var e=we(b,a.state.keyMaps[d],c);if(e)return e}return a.options.extraKeys&&we(b,a.options.extraKeys,c)||we(b,a.options.keyMap,c)}function zd(a,b,c,d){var e=a.state.keySeq;if(e){if(xe(b))return"handled";yd.set(50,function(){a.state.keySeq==e&&(a.state.keySeq=null,$c(a))}),b=e+" "+b}var f=xd(a,b,d);return"multi"==f&&(a.state.keySeq=b),"handled"==f&&ug(a,"
keyHandled",a,b,c),("handled"==f||"multi"==f)&&(kg(c),Sb(a)),e&&!f&&/\'$/.test(b)?(kg(c),!0):!!f}function Ad(a,b){var c=ye(b,!0);return c?b.shiftKey&&!a.state.keySeq?zd(a,"Shift-"+c,b,function(b){return wd(a,b,!0)})||zd(a,c,b,function(b){return("string"==typeof b?/^go[A-Z]/.test(b):b.motion)?wd(a,b):void 0}):zd(a,c,b,function(b){return wd(a,b)}):!1}function Bd(a,b,c){return zd(a,"'"+c+"'",b,function(b){return wd(a,b,!0)})}function Dd(a){var b=this;if(ad(b),!wg(b,a)){d&&11>e&&27==a.keyCode&&(a.returnValue=!1);var c=a.keyCode;b.display.shift=16==c||a.shiftKey;var f=Ad(b,a);i&&(Cd=f?c:null,!f&&88==c&&!sh&&(p?a.metaKey:a.ctrlKey)&&b.replaceSelection("",null,"cut")),18!=c||/\bCodeMirror-crosshair\b/.test(b.display.lineDiv.className)||Ed(b)}}function Ed(a){function c(a){18!=a.keyCode&&a.altKey||(ch(b,"CodeMirror-crosshair"),rg(document,"keyup",c),rg(document,"mouseover",c))}var b=a.display.lineDiv;dh(b,"CodeMirror-crosshair"),qg(document,"keyup",c),qg(document,"mouseover",c)}function Fd(a
){16==a.keyCode&&(this.doc.sel.shift=!1),wg(this,a)}function Gd(a){var b=this;if(!(wg(b,a)||a.ctrlKey&&!a.altKey||p&&a.metaKey)){var c=a.keyCode,f=a.charCode;if(i&&c==Cd)return Cd=null,kg(a),void 0;if(!(i&&(!a.which||a.which<10)||k)||!Ad(b,a)){var g=String.fromCharCode(null==f?c:f);Bd(b,a,g)||(d&&e>=9&&(b.display.inputHasSelection=null),Xc(b))}}}function Hd(a){"nocursor"!=a.options.readOnly&&(a.state.focused||(sg(a,"focus",a),a.state.focused=!0,dh(a.display.wrapper,"CodeMirror-focused"),a.curOp||a.display.selForContextMenu==a.doc.sel||($c(a),f&&setTimeout(Qg($c,a,!0),0))),Wc(a),Sb(a))}function Id(a){a.state.focused&&(sg(a,"blur",a),a.state.focused=!1,ch(a.display.wrapper,"CodeMirror-focused")),clearInterval(a.display.blinker),setTimeout(function(){a.state.focused||(a.display.shift=!1)},150)}function Jd(a,b){function m(){if(null!=c.input.selectionStart){var b=a.somethingSelected(),d=c.input.value="\u200b"+(b?c.input.value:"");c.prevInput=b?"":"\u200b",c.input.selectionStart=1,c.input
.selectionEnd=d.length,c.selForContextMenu=a.doc.sel}}function n(){if(c.inputDiv.style.position="relative",c.input.style.cssText=k,d&&9>e&&(c.scrollbarV.scrollTop=c.scroller.scrollTop=h),Wc(a),null!=c.input.selectionStart){(!d||d&&9>e)&&m();var b=0,f=function(){c.selForContextMenu==a.doc.sel&&0==c.input.selectionStart?Kc(a,te.selectAll)(a):b++<10?c.detectingSelectAll=setTimeout(f,500):$c(a)};c.detectingSelectAll=setTimeout(f,200)}}if(!wg(a,b,"contextmenu")){var c=a.display;if(!ed(c,b)&&!Kd(a,b)){var g=fd(a,b),h=c.scroller.scrollTop;if(g&&!i){var j=a.options.resetSelectionOnContextMenu;j&&-1==a.doc.sel.contains(g)&&Kc(a,Hb)(a.doc,ub(g),Cg);var k=c.input.style.cssText;if(c.inputDiv.style.position="absolute",c.input.style.cssText="position: fixed; width: 30px; height: 30px; top: "+(b.clientY-5)+"px; left: "+(b.clientX-5)+"px; z-index: 1000; background: "+(d?"rgba(255, 255, 255, .05)":"transparent")+"; outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter
: alpha(opacity=5);",f)var l=window.scrollY;if(_c(a),f&&window.scrollTo(null,l),$c(a),a.somethingSelected()||(c.input.value=c.prevInput=" "),c.selForContextMenu=a.doc.sel,clearTimeout(c.detectingSelectAll),d&&e>=9&&m(),t){ng(b);var o=function(){rg(window,"mouseup",o),setTimeout(n,20)};qg(window,"mouseup",o)}else setTimeout(n,50)}}}}function Kd(a,b){return yg(a,"gutterContextMenu")?md(a,b,"gutterContextMenu",!1,sg):!1}function Md(a,b){if(nb(a,b.from)<0)return a;if(nb(a,b.to)<=0)return Ld(b);var c=a.line+b.text.length-(b.to.line-b.from.line)-1,d=a.ch;return a.line==b.to.line&&(d+=Ld(b).ch-b.to.ch),mb(c,d)}function Nd(a,b){for(var c=[],d=0;d<a.sel.ranges.length;d++){var e=a.sel.ranges[d];c.push(new sb(Md(e.anchor,b),Md(e.head,b)))}return tb(c,a.sel.primIndex)}function Od(a,b,c){return a.line==b.line?mb(c.line,a.ch-b.ch+c.ch):mb(c.line+(a.line-b.line),a.ch)}function Pd(a,b,c){for(var d=[],e=mb(a.first,0),f=e,g=0;g<b.length;g++){var h=b[g],i=Od(h.from,e,f),j=Od(Ld(h),e,f);if(e=h.to,f=j,"
around"==c){var k=a.sel.ranges[g],l=nb(k.head,k.anchor)<0;d[g]=new sb(l?j:i,l?i:j)}else d[g]=new sb(i,i)}return new rb(d,a.sel.primIndex)}function Qd(a,b,c){var d={canceled:!1,from:b.from,to:b.to,text:b.text,origin:b.origin,cancel:function(){this.canceled=!0}};return c&&(d.update=function(b,c,d,e){b&&(this.from=wb(a,b)),c&&(this.to=wb(a,c)),d&&(this.text=d),void 0!==e&&(this.origin=e)}),sg(a,"beforeChange",a,d),a.cm&&sg(a.cm,"beforeChange",a.cm,d),d.canceled?null:{from:d.from,to:d.to,text:d.text,origin:d.origin}}function Rd(a,b,c){if(a.cm){if(!a.cm.curOp)return Kc(a.cm,Rd)(a,b,c);if(a.cm.state.suppressEdits)return}if(!(yg(a,"beforeChange")||a.cm&&yg(a.cm,"beforeChange"))||(b=Qd(a,b,!0))){var d=u&&!c&&Se(a,b.from,b.to);if(d)for(var e=d.length-1;e>=0;--e)Sd(a,{from:d[e].from,to:d[e].to,text:e?[""]:b.text});else Sd(a,b)}}function Sd(a,b){if(1!=b.text.length||""!=b.text[0]||0!=nb(b.from,b.to)){var c=Nd(a,b);_f(a,b,c,a.cm?a.cm.curOp.id:0/0),Vd(a,b,c,Pe(a,b));var d=[];Nf(a,function(a,c){c
||-1!=Mg(d,a.history)||(jg(a.history,b),d.push(a.history)),Vd(a,b,null,Pe(a,b))})}}function Td(a,b,c){if(!a.cm||!a.cm.state.suppressEdits){for(var e,d=a.history,f=a.sel,g="undo"==b?d.done:d.undone,h="undo"==b?d.undone:d.done,i=0;i<g.length&&(e=g[i],c?!e.ranges||e.equals(a.sel):e.ranges);i++);if(i!=g.length){for(d.lastOrigin=d.lastSelOrigin=null;e=g.pop(),e.ranges;){if(cg(e,h),c&&!e.equals(a.sel))return Hb(a,e,{clearRedo:!1}),void 0;f=e}var j=[];cg(f,h),h.push({changes:j,generation:d.generation}),d.generation=e.generation||++d.maxGeneration;for(var k=yg(a,"beforeChange")||a.cm&&yg(a.cm,"beforeChange"),i=e.changes.length-1;i>=0;--i){var l=e.changes[i];if(l.origin=b,k&&!Qd(a,l,!1))return g.length=0,void 0;j.push(Yf(a,l));var m=i?Nd(a,l):Kg(g);Vd(a,l,m,Re(a,l)),!i&&a.cm&&a.cm.scrollIntoView({from:l.from,to:Ld(l)});var n=[];Nf(a,function(a,b){b||-1!=Mg(n,a.history)||(jg(a.history,l),n.push(a.history)),Vd(a,l,null,Re(a,l))})}}}}function Ud(a,b){if(0!=b&&(a.first+=b,a.sel=new rb(Ng(a.sel.r
anges,function(a){return new sb(mb(a.anchor.line+b,a.anchor.ch),mb(a.head.line+b,a.head.ch))}),a.sel.primIndex),a.cm)){Pc(a.cm,a.first,a.first-b,b);for(var c=a.cm.display,d=c.viewFrom;d<c.viewTo;d++)Qc(a.cm,d,"gutter")}}function Vd(a,b,c,d){if(a.cm&&!a.cm.curOp)return Kc(a.cm,Vd)(a,b,c,d);if(b.to.line<a.first)return Ud(a,b.text.length-1-(b.to.line-b.from.line)),void 0;if(!(b.from.line>a.lastLine())){if(b.from.line<a.first){var e=b.text.length-1-(a.first-b.from.line);Ud(a,e),b={from:mb(a.first,0),to:mb(b.to.line+e,b.to.ch),text:[Kg(b.text)],origin:b.origin}}var f=a.lastLine();b.to.line>f&&(b={from:b.from,to:mb(f,Pf(a,f).text.length),text:[b.text[0]],origin:b.origin}),b.removed=Qf(a,b.from,b.to),c||(c=Nd(a,b)),a.cm?Wd(a.cm,b,d):Gf(a,b,d),Ib(a,c,Cg)}}function Wd(a,b,c){var d=a.doc,e=a.display,f=b.from,g=b.to,h=!1,i=f.line;a.options.lineWrapping||(i=Tf(af(Pf(d,f.line))),d.iter(i,g.line+1,function(a){return a==e.maxLine?(h=!0,!0):void 0})),d.sel.contains(b.from,b.to)>-1&&xg(a),Gf(d,b,c,B
(a)),a.options.lineWrapping||(d.iter(i,f.line+b.text.length,function(a){var b=H(a);b>e.maxLineLength&&(e.maxLine=a,e.maxLineLength=b,e.maxLineChanged=!0,h=!1)}),h&&(a.curOp.updateMaxLine=!0)),d.frontier=Math.min(d.frontier,f.line),Tb(a,400);var j=b.text.length-(g.line-f.line)-1;f.line!=g.line||1!=b.text.length||Ff(a.doc,b)?Pc(a,f.line,g.line+1,j):Qc(a,f.line,"text");var k=yg(a,"changes"),l=yg(a,"change");if(l||k){var m={from:f,to:g,text:b.text,removed:b.removed,origin:b.origin};l&&ug(a,"change",a,m),k&&(a.curOp.changeObjs||(a.curOp.changeObjs=[])).push(m)}a.display.selForContextMenu=null}function Xd(a,b,c,d,e){if(d||(d=c),nb(d,c)<0){var f=d;d=c,c=f}"string"==typeof b&&(b=qh(b)),Rd(a,{from:c,to:d,text:b,origin:e})}function Yd(a,b){var c=a.display,d=c.sizer.getBoundingClientRect(),e=null;if(b.top+d.top<0?e=!0:b.bottom+d.top>(window.innerHeight||document.documentElement.clientHeight)&&(e=!1),null!=e&&!m){var f=Xg("div","\u200b",null,"position: absolute; top: "+(b.top-c.viewOffset-Xb(a.
display))+"px; height: "+(b.bottom-b.top+Ag)+"px; left: "+b.left+"px; width: 2px;");a.display.lineSpace.appendChild(f),f.scrollIntoView(e),a.display.lineSpace.removeChild(f)}}function Zd(a,b,c,d){null==d&&(d=0);for(var e=0;5>e;e++){var f=!1,g=qc(a,b),h=c&&c!=b?qc(a,c):g,i=_d(a,Math.min(g.left,h.left),Math.min(g.top,h.top)-d,Math.max(g.left,h.left),Math.max(g.bottom,h.bottom)+d),j=a.doc.scrollTop,k=a.doc.scrollLeft;if(null!=i.scrollTop&&(rd(a,i.scrollTop),Math.abs(a.doc.scrollTop-j)>1&&(f=!0)),null!=i.scrollLeft&&(sd(a,i.scrollLeft),Math.abs(a.doc.scrollLeft-k)>1&&(f=!0)),!f)return g}}function $d(a,b,c,d,e){var f=_d(a,b,c,d,e);null!=f.scrollTop&&rd(a,f.scrollTop),null!=f.scrollLeft&&sd(a,f.scrollLeft)}function _d(a,b,c,d,e){var f=a.display,g=wc(a.display);0>c&&(c=0);var h=a.curOp&&null!=a.curOp.scrollTop?a.curOp.scrollTop:f.scroller.scrollTop,i=f.scroller.clientHeight-Ag,j={};e-c>i&&(e=c+i);var k=a.doc.height+Yb(f),l=g>c,m=e>k-g;if(h>c)j.scrollTop=l?0:c;else if(e>h+i){var n=Math.min(
c,(m?k:e)-i);n!=h&&(j.scrollTop=n)}var o=a.curOp&&null!=a.curOp.scrollLeft?a.curOp.scrollLeft:f.scroller.scrollLeft,p=f.scroller.clientWidth-Ag-f.gutters.offsetWidth,q=d-b>p;return q&&(d=b+p),10>b?j.scrollLeft=0:o>b?j.scrollLeft=Math.max(0,b-(q?0:10)):d>p+o-3&&(j.scrollLeft=d+(q?0:10)-p),j}function ae(a,b,c){(null!=b||null!=c)&&ce(a),null!=b&&(a.curOp.scrollLeft=(null==a.curOp.scrollLeft?a.doc.scrollLeft:a.curOp.scrollLeft)+b),null!=c&&(a.curOp.scrollTop=(null==a.curOp.scrollTop?a.doc.scrollTop:a.curOp.scrollTop)+c)}function be(a){ce(a);var b=a.getCursor(),c=b,d=b;a.options.lineWrapping||(c=b.ch?mb(b.line,b.ch-1):b,d=mb(b.line,b.ch+1)),a.curOp.scrollToPos={from:c,to:d,margin:a.options.cursorScrollMargin,isCursor:!0}}function ce(a){var b=a.curOp.scrollToPos;if(b){a.curOp.scrollToPos=null;var c=rc(a,b.from),d=rc(a,b.to),e=_d(a,Math.min(c.left,d.left),Math.min(c.top,d.top)-b.margin,Math.max(c.right,d.right),Math.max(c.bottom,d.bottom)+b.margin);a.scrollTo(e.scrollLeft,e.scrollTop)}}fun
ction de(a,b,c,d){var f,e=a.doc;null==c&&(c="add"),"smart"==c&&(e.mode.indent?f=Wb(a,b):c="prev");var g=a.options.tabSize,h=Pf(e,b),i=Gg(h.text,null,g);h.stateAfter&&(h.stateAfter=null);var k,j=h.text.match(/^\s*/)[0];if(d||/\S/.test(h.text)){if("smart"==c&&(k=e.mode.indent(f,h.text.slice(j.length),h.text),k==Bg||k>150)){if(!d)return;c="prev"}}else k=0,c="not";"prev"==c?k=b>e.first?Gg(Pf(e,b-1).text,null,g):0:"add"==c?k=i+a.options.indentUnit:"subtract"==c?k=i-a.options.indentUnit:"number"==typeof c&&(k=i+c),k=Math.max(0,k);var l="",m=0;if(a.options.indentWithTabs)for(var n=Math.floor(k/g);n;--n)m+=g,l+=" ";if(k>m&&(l+=Jg(k-m)),l!=j)Xd(e,l,mb(b,0),mb(b,j.length),"+input");else for(var n=0;n<e.sel.ranges.length;n++){var o=e.sel.ranges[n];if(o.head.line==b&&o.head.ch<j.length){var m=mb(b,j.length);Db(e,n,new sb(m,m));break}}h.stateAfter=null}function ee(a,b,c,d){var e=b,f=b;return"number"==typeof b?f=Pf(a,vb(a,b)):e=Tf(b),null==e?null:(d(f,e)&&a.cm&&Qc(a.cm,e,c),f)}function fe(a,b){fo
r(var c=a.doc.sel.ranges,d=[],e=0;e<c.length;e++){for(var f=b(c[e]);d.length&&nb(f.from,Kg(d).to)<=0;){var g=d.pop();if(nb(g.from,f.from)<0){f.from=g.from;break}}d.push(f)}Jc(a,function(){for(var b=d.length-1;b>=0;b--)Xd(a.doc,"",d[b].from,d[b].to,"+delete");be(a)})}function ge(a,b,c,d,e){function k(){var b=f+c;return b<a.first||b>=a.first+a.size?j=!1:(f=b,i=Pf(a,b))}function l(a){var b=(e?Ih:Jh)(i,g,c,!0);if(null==b){if(a||!k())return j=!1;g=e?(0>c?Ah:zh)(i):0>c?i.text.length:0}else g=b;return!0}var f=b.line,g=b.ch,h=c,i=Pf(a,f),j=!0;if("char"==d)l();else if("column"==d)l(!0);else if("word"==d||"group"==d)for(var m=null,n="group"==d,o=a.cm&&a.cm.getHelper(b,"wordChars"),p=!0;!(0>c)||l(!p);p=!1){var q=i.text.charAt(g)||"\n",r=Tg(q,o)?"w":n&&"\n"==q?"n":!n||/\s/.test(q)?null:"p";if(!n||p||r||(r="s"),m&&m!=r){0>c&&(c=1,l());break}if(r&&(m=r),c>0&&!l(!p))break}var s=Mb(a,mb(f,g),h,!0);return j||(s.hitSide=!0),s}function he(a,b,c,d){var g,e=a.doc,f=b.left;if("page"==d){var h=Math.min(a.
display.wrapper.clientHeight,window.innerHeight||document.documentElement.clientHeight);g=b.top+c*(h-(0>c?1.5:.5)*wc(a.display))}else"line"==d&&(g=c>0?b.bottom+3:b.top-3);for(;;){var i=tc(a,f,g);if(!i.outside)break;if(0>c?0>=g:g>=e.height){i.hitSide=!0;break}g+=5*c}return i}function ke(a,b,c,d){w.defaults[a]=b,c&&(je[a]=d?function(a,b,d){d!=le&&c(a,b,d)}:c)}function ve(a){for(var c,d,e,f,b=a.split(/-(?!$)/),a=b[b.length-1],g=0;g<b.length-1;g++){var h=b[g];if(/^(cmd|meta|m)$/i.test(h))f=!0;else if(/^a(lt)?$/i.test(h))c=!0;else if(/^(c|ctrl|control)$/i.test(h))d=!0;else{if(!/^s(hift)$/i.test(h))throw new Error("Unrecognized modifier name: "+h);e=!0}}return c&&(a="Alt-"+a),d&&(a="Ctrl-"+a),f&&(a="Cmd-"+a),e&&(a="Shift-"+a),a}function ze(a){return"string"==typeof a?ue[a]:a}function De(a,b,c,d,e){if(d&&d.shared)return Fe(a,b,c,d,e);if(a.cm&&!a.cm.curOp)return Kc(a.cm,De)(a,b,c,d,e);var f=new Be(a,e),g=nb(b,c);if(d&&Pg(d,f,!1),g>0||0==g&&f.clearWhenEmpty!==!1)return f;if(f.replacedWith&&(
f.collapsed=!0,f.widgetNode=Xg("span",[f.replacedWith],"CodeMirror-widget"),d.handleMouseEvents||(f.widgetNode.ignoreEvents=!0),d.insertLeft&&(f.widgetNode.insertLeft=!0)),f.collapsed){if(_e(a,b.line,b,c,f)||b.line!=c.line&&_e(a,c.line,b,c,f))throw new Error("Inserting collapsed marker partially overlapping an existing one");
+v=!0}f.addToHistory&&_f(a,{from:b,to:c,origin:"markText"},a.sel,0/0);var j,h=b.line,i=a.cm;if(a.iter(h,c.line+1,function(a){i&&f.collapsed&&!i.options.lineWrapping&&af(a)==i.display.maxLine&&(j=!0),f.collapsed&&h!=b.line&&Sf(a,0),Me(a,new Je(f,h==b.line?b.ch:null,h==c.line?c.ch:null)),++h}),f.collapsed&&a.iter(b.line,c.line+1,function(b){ef(a,b)&&Sf(b,0)}),f.clearOnEnter&&qg(f,"beforeCursorEnter",function(){f.clear()}),f.readOnly&&(u=!0,(a.history.done.length||a.history.undone.length)&&a.clearHistory()),f.collapsed&&(f.id=++Ce,f.atomic=!0),i){if(j&&(i.curOp.updateMaxLine=!0),f.collapsed)Pc(i,b.line,c.line+1);else if(f.className||f.title||f.startStyle||f.endStyle)for(var k=b.line;k<=c.line;k++)Qc(i,k,"text");f.atomic&&Kb(i.doc),ug(i,"markerAdded",i,f)}return f}function Fe(a,b,c,d,e){d=Pg(d),d.shared=!1;var f=[De(a,b,c,d,e)],g=f[0],h=d.widgetNode;return Nf(a,function(a){h&&(d.widgetNode=h.cloneNode(!0)),f.push(De(a,wb(a,b),wb(a,c),d,e));for(var i=0;i<a.linked.length;++i)if(a.linked[i]
.isParent)return;g=Kg(f)}),new Ee(f,g)}function Ge(a){return a.findMarks(mb(a.first,0),a.clipPos(mb(a.lastLine())),function(a){return a.parent})}function He(a,b){for(var c=0;c<b.length;c++){var d=b[c],e=d.find(),f=a.clipPos(e.from),g=a.clipPos(e.to);if(nb(f,g)){var h=De(a,f,g,d.primary,d.primary.type);d.markers.push(h),h.parent=d}}}function Ie(a){for(var b=0;b<a.length;b++){var c=a[b],d=[c.primary.doc];Nf(c.primary.doc,function(a){d.push(a)});for(var e=0;e<c.markers.length;e++){var f=c.markers[e];-1==Mg(d,f.doc)&&(f.parent=null,c.markers.splice(e--,1))}}}function Je(a,b,c){this.marker=a,this.from=b,this.to=c}function Ke(a,b){if(a)for(var c=0;c<a.length;++c){var d=a[c];if(d.marker==b)return d}}function Le(a,b){for(var c,d=0;d<a.length;++d)a[d]!=b&&(c||(c=[])).push(a[d]);return c}function Me(a,b){a.markedSpans=a.markedSpans?a.markedSpans.concat([b]):[b],b.marker.attachLine(a)}function Ne(a,b,c){if(a)for(var e,d=0;d<a.length;++d){var f=a[d],g=f.marker,h=null==f.from||(g.inclusiveLeft?f
.from<=b:f.from<b);if(h||f.from==b&&"bookmark"==g.type&&(!c||!f.marker.insertLeft)){var i=null==f.to||(g.inclusiveRight?f.to>=b:f.to>b);(e||(e=[])).push(new Je(g,f.from,i?null:f.to))}}return e}function Oe(a,b,c){if(a)for(var e,d=0;d<a.length;++d){var f=a[d],g=f.marker,h=null==f.to||(g.inclusiveRight?f.to>=b:f.to>b);if(h||f.from==b&&"bookmark"==g.type&&(!c||f.marker.insertLeft)){var i=null==f.from||(g.inclusiveLeft?f.from<=b:f.from<b);(e||(e=[])).push(new Je(g,i?null:f.from-b,null==f.to?null:f.to-b))}}return e}function Pe(a,b){var c=yb(a,b.from.line)&&Pf(a,b.from.line).markedSpans,d=yb(a,b.to.line)&&Pf(a,b.to.line).markedSpans;if(!c&&!d)return null;var e=b.from.ch,f=b.to.ch,g=0==nb(b.from,b.to),h=Ne(c,e,g),i=Oe(d,f,g),j=1==b.text.length,k=Kg(b.text).length+(j?e:0);if(h)for(var l=0;l<h.length;++l){var m=h[l];if(null==m.to){var n=Ke(i,m.marker);n?j&&(m.to=null==n.to?null:n.to+k):m.to=e}}if(i)for(var l=0;l<i.length;++l){var m=i[l];if(null!=m.to&&(m.to+=k),null==m.from){var n=Ke(h,m.mark
er);n||(m.from=k,j&&(h||(h=[])).push(m))}else m.from+=k,j&&(h||(h=[])).push(m)}h&&(h=Qe(h)),i&&i!=h&&(i=Qe(i));var o=[h];if(!j){var q,p=b.text.length-2;if(p>0&&h)for(var l=0;l<h.length;++l)null==h[l].to&&(q||(q=[])).push(new Je(h[l].marker,null,null));for(var l=0;p>l;++l)o.push(q);o.push(i)}return o}function Qe(a){for(var b=0;b<a.length;++b){var c=a[b];null!=c.from&&c.from==c.to&&c.marker.clearWhenEmpty!==!1&&a.splice(b--,1)}return a.length?a:null}function Re(a,b){var c=fg(a,b),d=Pe(a,b);if(!c)return d;if(!d)return c;for(var e=0;e<c.length;++e){var f=c[e],g=d[e];if(f&&g)a:for(var h=0;h<g.length;++h){for(var i=g[h],j=0;j<f.length;++j)if(f[j].marker==i.marker)continue a;f.push(i)}else g&&(c[e]=g)}return c}function Se(a,b,c){var d=null;if(a.iter(b.line,c.line+1,function(a){if(a.markedSpans)for(var b=0;b<a.markedSpans.length;++b){var c=a.markedSpans[b].marker;!c.readOnly||d&&-1!=Mg(d,c)||(d||(d=[])).push(c)}}),!d)return null;for(var e=[{from:b,to:c}],f=0;f<d.length;++f)for(var g=d[f],h=
g.find(0),i=0;i<e.length;++i){var j=e[i];if(!(nb(j.to,h.from)<0||nb(j.from,h.to)>0)){var k=[i,1],l=nb(j.from,h.from),m=nb(j.to,h.to);(0>l||!g.inclusiveLeft&&!l)&&k.push({from:j.from,to:h.from}),(m>0||!g.inclusiveRight&&!m)&&k.push({from:h.to,to:j.to}),e.splice.apply(e,k),i+=k.length-1}}return e}function Te(a){var b=a.markedSpans;if(b){for(var c=0;c<b.length;++c)b[c].marker.detachLine(a);a.markedSpans=null}}function Ue(a,b){if(b){for(var c=0;c<b.length;++c)b[c].marker.attachLine(a);a.markedSpans=b}}function Ve(a){return a.inclusiveLeft?-1:0}function We(a){return a.inclusiveRight?1:0}function Xe(a,b){var c=a.lines.length-b.lines.length;if(0!=c)return c;var d=a.find(),e=b.find(),f=nb(d.from,e.from)||Ve(a)-Ve(b);if(f)return-f;var g=nb(d.to,e.to)||We(a)-We(b);return g?g:b.id-a.id}function Ye(a,b){var d,c=v&&a.markedSpans;if(c)for(var e,f=0;f<c.length;++f)e=c[f],e.marker.collapsed&&null==(b?e.from:e.to)&&(!d||Xe(d,e.marker)<0)&&(d=e.marker);return d}function Ze(a){return Ye(a,!0)}function
$e(a){return Ye(a,!1)}function _e(a,b,c,d,e){var f=Pf(a,b),g=v&&f.markedSpans;if(g)for(var h=0;h<g.length;++h){var i=g[h];if(i.marker.collapsed){var j=i.marker.find(0),k=nb(j.from,c)||Ve(i.marker)-Ve(e),l=nb(j.to,d)||We(i.marker)-We(e);if(!(k>=0&&0>=l||0>=k&&l>=0)&&(0>=k&&(nb(j.to,c)>0||i.marker.inclusiveRight&&e.inclusiveLeft)||k>=0&&(nb(j.from,d)<0||i.marker.inclusiveLeft&&e.inclusiveRight)))return!0}}}function af(a){for(var b;b=Ze(a);)a=b.find(-1,!0).line;return a}function bf(a){for(var b,c;b=$e(a);)a=b.find(1,!0).line,(c||(c=[])).push(a);return c}function cf(a,b){var c=Pf(a,b),d=af(c);return c==d?b:Tf(d)}function df(a,b){if(b>a.lastLine())return b;var d,c=Pf(a,b);if(!ef(a,c))return b;for(;d=$e(c);)c=d.find(1,!0).line;return Tf(c)+1}function ef(a,b){var c=v&&b.markedSpans;if(c)for(var d,e=0;e<c.length;++e)if(d=c[e],d.marker.collapsed){if(null==d.from)return!0;if(!d.marker.widgetNode&&0==d.from&&d.marker.inclusiveLeft&&ff(a,b,d))return!0}}function ff(a,b,c){if(null==c.to){var d=c
.marker.find(1,!0);return ff(a,d.line,Ke(d.line.markedSpans,c.marker))}if(c.marker.inclusiveRight&&c.to==b.text.length)return!0;for(var e,f=0;f<b.markedSpans.length;++f)if(e=b.markedSpans[f],e.marker.collapsed&&!e.marker.widgetNode&&e.from==c.to&&(null==e.to||e.to!=c.from)&&(e.marker.inclusiveLeft||c.marker.inclusiveRight)&&ff(a,b,e))return!0}function hf(a,b,c){Vf(b)<(a.curOp&&a.curOp.scrollTop||a.doc.scrollTop)&&ae(a,null,c)}function jf(a){if(null!=a.height)return a.height;if(!_g(document.body,a.node)){var b="position: relative;";a.coverGutter&&(b+="margin-left: -"+a.cm.getGutterElement().offsetWidth+"px;"),$g(a.cm.display.measure,Xg("div",[a.node],null,b))}return a.height=a.node.offsetHeight}function kf(a,b,c,d){var e=new gf(a,c,d);return e.noHScroll&&(a.display.alignWidgets=!0),ee(a.doc,b,"widget",function(b){var c=b.widgets||(b.widgets=[]);if(null==e.insertAt?c.push(e):c.splice(Math.min(c.length-1,Math.max(0,e.insertAt)),0,e),e.line=b,!ef(a.doc,b)){var d=Vf(b)<a.doc.scrollTop;Sf
(b,b.height+jf(e)),d&&ae(a,null,e.height),a.curOp.forceUpdate=!0}return!0}),e}function mf(a,b,c,d){a.text=b,a.stateAfter&&(a.stateAfter=null),a.styles&&(a.styles=null),null!=a.order&&(a.order=null),Te(a),Ue(a,c);var e=d?d(a):1;e!=a.height&&Sf(a,e)}function nf(a){a.parent=null,Te(a)}function of(a,b){if(a)for(;;){var c=a.match(/(?:^|\s+)line-(background-)?(\S+)/);if(!c)break;a=a.slice(0,c.index)+a.slice(c.index+c[0].length);var d=c[1]?"bgClass":"textClass";null==b[d]?b[d]=c[2]:new RegExp("(?:^|s)"+c[2]+"(?:$|s)").test(b[d])||(b[d]+=" "+c[2])}return a}function pf(a,b){if(a.blankLine)return a.blankLine(b);if(a.innerMode){var c=w.innerMode(a,b);return c.mode.blankLine?c.mode.blankLine(c.state):void 0}}function qf(a,b,c){for(var d=0;10>d;d++){var e=a.token(b,c);if(b.pos>b.start)return e}throw new Error("Mode "+a.name+" failed to advance stream.")}function rf(a,b,c,d,e,f,g){var h=c.flattenSpans;null==h&&(h=a.options.flattenSpans);var l,i=0,j=null,k=new Ae(b,a.options.tabSize);for(""==b&&of
(pf(c,d),f);!k.eol();){if(k.pos>a.options.maxHighlightLength?(h=!1,g&&uf(a,b,d,k.pos),k.pos=b.length,l=null):l=of(qf(c,k,d),f),a.options.addModeClass){var m=w.innerMode(c,d).mode.name;m&&(l="m-"+(l?m+" "+l:m))}h&&j==l||(i<k.start&&e(k.start,j),i=k.start,j=l),k.start=k.pos}for(;i<k.pos;){var n=Math.min(k.pos,i+5e4);e(n,j),i=n}}function sf(a,b,c,d){var e=[a.state.modeGen],f={};rf(a,b.text,a.doc.mode,c,function(a,b){e.push(a,b)},f,d);for(var g=0;g<a.state.overlays.length;++g){var h=a.state.overlays[g],i=1,j=0;rf(a,b.text,h.mode,!0,function(a,b){for(var c=i;a>j;){var d=e[i];d>a&&e.splice(i,1,a,e[i+1],d),i+=2,j=Math.min(a,d)}if(b)if(h.opaque)e.splice(c,i-c,a,"cm-overlay "+b),i=c+2;else for(;i>c;c+=2){var f=e[c+1];e[c+1]=(f?f+" ":"")+"cm-overlay "+b}},f)}return{styles:e,classes:f.bgClass||f.textClass?f:null}}function tf(a,b,c){if(!b.styles||b.styles[0]!=a.state.modeGen){var d=sf(a,b,b.stateAfter=Wb(a,Tf(b)));b.styles=d.styles,d.classes?b.styleClasses=d.classes:b.styleClasses&&(b.styleClas
ses=null),c===a.doc.frontier&&a.doc.frontier++}return b.styles}function uf(a,b,c,d){var e=a.doc.mode,f=new Ae(b,a.options.tabSize);for(f.start=f.pos=d||0,""==b&&pf(e,c);!f.eol()&&f.pos<=a.options.maxHighlightLength;)qf(e,f,c),f.start=f.pos}function xf(a,b){if(!a||/^\s*$/.test(a))return null;var c=b.addModeClass?wf:vf;return c[a]||(c[a]=a.replace(/\S+/g,"cm-$&"))}function yf(a,b){var c=Xg("span",null,null,f?"padding-right: .1px":null),e={pre:Xg("pre",[c]),content:c,col:0,pos:0,cm:a};b.measure={};for(var g=0;g<=(b.rest?b.rest.length:0);g++){var i,h=g?b.rest[g-1]:b.line;e.pos=0,e.addToken=Af,(d||f)&&a.getOption("lineWrapping")&&(e.addToken=Bf(e.addToken)),ph(a.display.measure)&&(i=Wf(h))&&(e.addToken=Cf(e.addToken,i)),e.map=[];var j=b!=a.display.externalMeasured&&Tf(h);Ef(h,e,tf(a,h,j)),h.styleClasses&&(h.styleClasses.bgClass&&(e.bgClass=eh(h.styleClasses.bgClass,e.bgClass||"")),h.styleClasses.textClass&&(e.textClass=eh(h.styleClasses.textClass,e.textClass||""))),0==e.map.length&&e.map
.push(0,0,e.content.appendChild(nh(a.display.measure))),0==g?(b.measure.map=e.map,b.measure.cache={}):((b.measure.maps||(b.measure.maps=[])).push(e.map),(b.measure.caches||(b.measure.caches=[])).push({}))}return sg(a,"renderLine",a,b.line,e.pre),e.pre.className&&(e.textClass=eh(e.pre.className,e.textClass||"")),e}function zf(a){var b=Xg("span","\u2022","cm-invalidchar");return b.title="\\u"+a.charCodeAt(0).toString(16),b}function Af(a,b,c,f,g,h){if(b){var i=a.cm.options.specialChars,j=!1;if(i.test(b))for(var k=document.createDocumentFragment(),l=0;;){i.lastIndex=l;var m=i.exec(b),n=m?m.index-l:b.length-l;if(n){var o=document.createTextNode(b.slice(l,l+n));d&&9>e?k.appendChild(Xg("span",[o])):k.appendChild(o),a.map.push(a.pos,a.pos+n,o),a.col+=n,a.pos+=n}if(!m)break;if(l+=n+1," "==m[0]){var p=a.cm.options.tabSize,q=p-a.col%p,o=k.appendChild(Xg("span",Jg(q),"cm-tab"));a.col+=q}else{var o=a.cm.options.specialCharPlaceholder(m[0]);d&&9>e?k.appendChild(Xg("span",[o])):k.appendChild(o),a.
col+=1}a.map.push(a.pos,a.pos+1,o),a.pos++}else{a.col+=b.length;var k=document.createTextNode(b);a.map.push(a.pos,a.pos+b.length,k),d&&9>e&&(j=!0),a.pos+=b.length}if(c||f||g||j){var r=c||"";f&&(r+=f),g&&(r+=g);var s=Xg("span",[k],r);return h&&(s.title=h),a.content.appendChild(s)}a.content.appendChild(k)}}function Bf(a){function b(a){for(var b=" ",c=0;c<a.length-2;++c)b+=c%2?" ":"\xa0";return b+=" "}return function(c,d,e,f,g,h){a(c,d.replace(/ {3,}/g,b),e,f,g,h)}}function Cf(a,b){return function(c,d,e,f,g,h){e=e?e+" cm-force-border":"cm-force-border";for(var i=c.pos,j=i+d.length;;){for(var k=0;k<b.length;k++){var l=b[k];if(l.to>i&&l.from<=i)break}if(l.to>=j)return a(c,d,e,f,g,h);a(c,d.slice(0,l.to-i),e,f,null,h),f=null,d=d.slice(l.to-i),i=l.to}}}function Df(a,b,c,d){var e=!d&&c.widgetNode;e&&(a.map.push(a.pos,a.pos+b,e),a.content.appendChild(e)),a.pos+=b}function Ef(a,b,c){var d=a.markedSpans,e=a.text,f=0;if(d)for(var k,m,n,o,p,q,h=e.length,i=0,g=1,j="",l=0;;){if(l==i){m=n=o=p="",q=n
ull,l=1/0;for(var r=[],s=0;s<d.length;++s){var t=d[s],u=t.marker;t.from<=i&&(null==t.to||t.to>i)?(null!=t.to&&l>t.to&&(l=t.to,n=""),u.className&&(m+=" "+u.className),u.startStyle&&t.from==i&&(o+=" "+u.startStyle),u.endStyle&&t.to==l&&(n+=" "+u.endStyle),u.title&&!p&&(p=u.title),u.collapsed&&(!q||Xe(q.marker,u)<0)&&(q=t)):t.from>i&&l>t.from&&(l=t.from),"bookmark"==u.type&&t.from==i&&u.widgetNode&&r.push(u)}if(q&&(q.from||0)==i&&(Df(b,(null==q.to?h+1:q.to)-i,q.marker,null==q.from),null==q.to))return;if(!q&&r.length)for(var s=0;s<r.length;++s)Df(b,0,r[s])}if(i>=h)break;for(var v=Math.min(h,l);;){if(j){var w=i+j.length;if(!q){var x=w>v?j.slice(0,v-i):j;b.addToken(b,x,k?k+m:m,o,i+x.length==l?n:"",p)}if(w>=v){j=j.slice(v-i),i=v;break}i=w,o=""}j=e.slice(f,f=c[g++]),k=xf(c[g++],b.cm.options)}}else for(var g=1;g<c.length;g+=2)b.addToken(b,e.slice(f,f=c[g]),xf(c[g+1],b.cm.options))}function Ff(a,b){return 0==b.from.ch&&0==b.to.ch&&""==Kg(b.text)&&(!a.cm||a.cm.options.wholeLineUpdateBefore)}fu
nction Gf(a,b,c,d){function e(a){return c?c[a]:null}function f(a,c,e){mf(a,c,e,d),ug(a,"change",a,b)}var g=b.from,h=b.to,i=b.text,j=Pf(a,g.line),k=Pf(a,h.line),l=Kg(i),m=e(i.length-1),n=h.line-g.line;if(Ff(a,b)){for(var o=0,p=[];o<i.length-1;++o)p.push(new lf(i[o],e(o),d));f(k,k.text,m),n&&a.remove(g.line,n),p.length&&a.insert(g.line,p)}else if(j==k)if(1==i.length)f(j,j.text.slice(0,g.ch)+l+j.text.slice(h.ch),m);else{for(var p=[],o=1;o<i.length-1;++o)p.push(new lf(i[o],e(o),d));p.push(new lf(l+j.text.slice(h.ch),m,d)),f(j,j.text.slice(0,g.ch)+i[0],e(0)),a.insert(g.line+1,p)}else if(1==i.length)f(j,j.text.slice(0,g.ch)+i[0]+k.text.slice(h.ch),e(0)),a.remove(g.line+1,n);else{f(j,j.text.slice(0,g.ch)+i[0],e(0)),f(k,l+k.text.slice(h.ch),m);for(var o=1,p=[];o<i.length-1;++o)p.push(new lf(i[o],e(o),d));n>1&&a.remove(g.line+1,n-1),a.insert(g.line+1,p)}ug(a,"change",a,b)}function Hf(a){this.lines=a,this.parent=null;for(var b=0,c=0;b<a.length;++b)a[b].parent=this,c+=a[b].height;this.height=c
}function If(a){this.children=a;for(var b=0,c=0,d=0;d<a.length;++d){var e=a[d];b+=e.chunkSize(),c+=e.height,e.parent=this}this.size=b,this.height=c,this.parent=null}function Nf(a,b,c){function d(a,e,f){if(a.linked)for(var g=0;g<a.linked.length;++g){var h=a.linked[g];if(h.doc!=e){var i=f&&h.sharedHist;(!c||i)&&(b(h.doc,i),d(h.doc,a,i))}}}d(a,null,!0)}function Of(a,b){if(b.cm)throw new Error("This document is already in use.");a.doc=b,b.cm=a,C(a),y(a),a.options.lineWrapping||I(a),a.options.mode=b.modeOption,Pc(a)}function Pf(a,b){if(b-=a.first,0>b||b>=a.size)throw new Error("There is no line "+(b+a.first)+" in the document.");for(var c=a;!c.lines;)for(var d=0;;++d){var e=c.children[d],f=e.chunkSize();if(f>b){c=e;break}b-=f}return c.lines[b]}function Qf(a,b,c){var d=[],e=b.line;return a.iter(b.line,c.line+1,function(a){var f=a.text;e==c.line&&(f=f.slice(0,c.ch)),e==b.line&&(f=f.slice(b.ch)),d.push(f),++e}),d}function Rf(a,b,c){var d=[];return a.iter(b,c,function(a){d.push(a.text)}),d}f
unction Sf(a,b){var c=b-a.height;if(c)for(var d=a;d;d=d.parent)d.height+=c}function Tf(a){if(null==a.parent)return null;for(var b=a.parent,c=Mg(b.lines,a),d=b.parent;d;b=d,d=d.parent)for(var e=0;d.children[e]!=b;++e)c+=d.children[e].chunkSize();return c+b.first}function Uf(a,b){var c=a.first;a:do{for(var d=0;d<a.children.length;++d){var e=a.children[d],f=e.height;if(f>b){a=e;continue a}b-=f,c+=e.chunkSize()}return c}while(!a.lines);for(var d=0;d<a.lines.length;++d){var g=a.lines[d],h=g.height;if(h>b)break;b-=h}return c+d}function Vf(a){a=af(a);for(var b=0,c=a.parent,d=0;d<c.lines.length;++d){var e=c.lines[d];if(e==a)break;b+=e.height}for(var f=c.parent;f;c=f,f=c.parent)for(var d=0;d<f.children.length;++d){var g=f.children[d];if(g==c)break;b+=g.height}return b}function Wf(a){var b=a.order;return null==b&&(b=a.order=Kh(a.text)),b}function Xf(a){this.done=[],this.undone=[],this.undoDepth=1/0,this.lastModTime=this.lastSelTime=0,this.lastOp=this.lastSelOp=null,this.lastOrigin=this.lastSe
lOrigin=null,this.generation=this.maxGeneration=a||1}function Yf(a,b){var c={from:ob(b.from),to:Ld(b),text:Qf(a,b.from,b.to)};return dg(a,c,b.from.line,b.to.line+1),Nf(a,function(a){dg(a,c,b.from.line,b.to.line+1)},!0),c}function Zf(a){for(;a.length;){var b=Kg(a);if(!b.ranges)break;a.pop()}}function $f(a,b){return b?(Zf(a.done),Kg(a.done)):a.done.length&&!Kg(a.done).ranges?Kg(a.done):a.done.length>1&&!a.done[a.done.length-2].ranges?(a.done.pop(),Kg(a.done)):void 0}function _f(a,b,c,d){var e=a.history;e.undone.length=0;var g,f=+new Date;if((e.lastOp==d||e.lastOrigin==b.origin&&b.origin&&("+"==b.origin.charAt(0)&&a.cm&&e.lastModTime>f-a.cm.options.historyEventDelay||"*"==b.origin.charAt(0)))&&(g=$f(e,e.lastOp==d))){var h=Kg(g.changes);0==nb(b.from,b.to)&&0==nb(b.from,h.to)?h.to=Ld(b):g.changes.push(Yf(a,b))}else{var i=Kg(e.done);for(i&&i.ranges||cg(a.sel,e.done),g={changes:[Yf(a,b)],generation:e.generation},e.done.push(g);e.done.length>e.undoDepth;)e.done.shift(),e.done[0].ranges||e.d
one.shift()}e.done.push(c),e.generation=++e.maxGeneration,e.lastModTime=e.lastSelTime=f,e.lastOp=e.lastSelOp=d,e.lastOrigin=e.lastSelOrigin=b.origin,h||sg(a,"historyAdded")}function ag(a,b,c,d){var e=b.charAt(0);return"*"==e||"+"==e&&c.ranges.length==d.ranges.length&&c.somethingSelected()==d.somethingSelected()&&new Date-a.history.lastSelTime<=(a.cm?a.cm.options.historyEventDelay:500)}function bg(a,b,c,d){var e=a.history,f=d&&d.origin;c==e.lastSelOp||f&&e.lastSelOrigin==f&&(e.lastModTime==e.lastSelTime&&e.lastOrigin==f||ag(a,f,Kg(e.done),b))?e.done[e.done.length-1]=b:cg(b,e.done),e.lastSelTime=+new Date,e.lastSelOrigin=f,e.lastSelOp=c,d&&d.clearRedo!==!1&&Zf(e.undone)}function cg(a,b){var c=Kg(b);c&&c.ranges&&c.equals(a)||b.push(a)}function dg(a,b,c,d){var e=b["spans_"+a.id],f=0;a.iter(Math.max(a.first,c),Math.min(a.first+a.size,d),function(c){c.markedSpans&&((e||(e=b["spans_"+a.id]={}))[f]=c.markedSpans),++f})}function eg(a){if(!a)return null;for(var c,b=0;b<a.length;++b)a[b].marke
r.explicitlyCleared?c||(c=a.slice(0,b)):c&&c.push(a[b]);return c?c.length?c:null:a}function fg(a,b){var c=b["spans_"+a.id];if(!c)return null;for(var d=0,e=[];d<b.text.length;++d)e.push(eg(c[d]));return e}function gg(a,b,c){for(var d=0,e=[];d<a.length;++d){var f=a[d];if(f.ranges)e.push(c?rb.prototype.deepCopy.call(f):f);else{var g=f.changes,h=[];e.push({changes:h});for(var i=0;i<g.length;++i){var k,j=g[i];if(h.push({from:j.from,to:j.to,text:j.text}),b)for(var l in j)(k=l.match(/^spans_(\d+)$/))&&Mg(b,Number(k[1]))>-1&&(Kg(h)[l]=j[l],delete j[l])}}}return e}function hg(a,b,c,d){c<a.line?a.line+=d:b<a.line&&(a.line=b,a.ch=0)}function ig(a,b,c,d){for(var e=0;e<a.length;++e){var f=a[e],g=!0;if(f.ranges){f.copied||(f=a[e]=f.deepCopy(),f.copied=!0);for(var h=0;h<f.ranges.length;h++)hg(f.ranges[h].anchor,b,c,d),hg(f.ranges[h].head,b,c,d)}else{for(var h=0;h<f.changes.length;++h){var i=f.changes[h];if(c<i.from.line)i.from=mb(i.from.line+d,i.from.ch),i.to=mb(i.to.line+d,i.to.ch);else if(b<=i.t
o.line){g=!1;break}}g||(a.splice(0,e+1),e=0)}}}function jg(a,b){var c=b.from.line,d=b.to.line,e=b.text.length-(d-c)-1;ig(a.done,c,d,e),ig(a.undone,c,d,e)}function mg(a){return null!=a.defaultPrevented?a.defaultPrevented:0==a.returnValue}function og(a){return a.target||a.srcElement}function pg(a){var b=a.which;return null==b&&(1&a.button?b=1:2&a.button?b=3:4&a.button&&(b=2)),p&&a.ctrlKey&&1==b&&(b=3),b}function ug(a,b){function f(a){return function(){a.apply(null,d)}}var c=a._handlers&&a._handlers[b];if(c){var e,d=Array.prototype.slice.call(arguments,2);yc?e=yc.delayedCallbacks:tg?e=tg:(e=tg=[],setTimeout(vg,0));for(var g=0;g<c.length;++g)e.push(f(c[g]))}}function vg(){var a=tg;tg=null;for(var b=0;b<a.length;++b)a[b]()}function wg(a,b,c){return sg(a,c||b.type,a,b),mg(b)||b.codemirrorIgnore}function xg(a){var b=a._handlers&&a._handlers.cursorActivity;if(b)for(var c=a.curOp.cursorActivityHandlers||(a.curOp.cursorActivityHandlers=[]),d=0;d<b.length;++d)-1==Mg(c,b[d])&&c.push(b[d])}funct
ion yg(a,b){var c=a._handlers&&a._handlers[b];return c&&c.length>0}function zg(a){a.prototype.on=function(a,b){qg(this,a,b)},a.prototype.off=function(a,b){rg(this,a,b)}}function Fg(){this.id=null}function Hg(a,b,c){for(var d=0,e=0;;){var f=a.indexOf(" ",d);-1==f&&(f=a.length);var g=f-d;if(f==a.length||e+g>=b)return d+Math.min(g,b-e);if(e+=f-d,e+=c-e%c,d=f+1,e>=b)return d}}function Jg(a){for(;Ig.length<=a;)Ig.push(Kg(Ig)+" ");return Ig[a]}function Kg(a){return a[a.length-1]}function Mg(a,b){for(var c=0;c<a.length;++c)if(a[c]==b)return c;return-1}function Ng(a,b){for(var c=[],d=0;d<a.length;d++)c[d]=b(a[d],d);return c}function Og(a,b){var c;if(Object.create)c=Object.create(a);else{var d=function(){};d.prototype=a,c=new d}return b&&Pg(b,c),c}function Pg(a,b,c){b||(b={});for(var d in a)!a.hasOwnProperty(d)||c===!1&&b.hasOwnProperty(d)||(b[d]=a[d]);return b}function Qg(a){var b=Array.prototype.slice.call(arguments,1);return function(){return a.apply(null,b)}}function Tg(a,b){return b?b.s
ource.indexOf("\\w")>-1&&Sg(a)?!0:b.test(a):Sg(a)}function Ug(a){for(var b in a)if(a.hasOwnProperty(b)&&a[b])return!1;return!0}function Wg(a){return a.charCodeAt(0)>=768&&Vg.test(a)}function Xg(a,b,c,d){var e=document.createElement(a);if(c&&(e.className=c),d&&(e.style.cssText=d),"string"==typeof b)e.appendChild(document.createTextNode(b));else if(b)for(var f=0;f<b.length;++f)e.appendChild(b[f]);return e}function Zg(a){for(var b=a.childNodes.length;b>0;--b)a.removeChild(a.firstChild);return a}function $g(a,b){return Zg(a).appendChild(b)}function _g(a,b){if(a.contains)return a.contains(b);for(;b=b.parentNode;)if(b==a)return!0}function ah(){return document.activeElement}function bh(a){return new RegExp("\\b"+a+"\\b\\s*")}function eh(a,b){for(var c=a.split(" "),d=0;d<c.length;d++)c[d]&&!bh(c[d]).test(b)&&(b+=" "+c[d]);return b}function fh(a){if(document.body.getElementsByClassName)for(var b=document.body.getElementsByClassName("CodeMirror"),c=0;c<b.length;c++){var d=b[c].CodeMirror;d&&a
(d)}}function hh(){gh||(ih(),gh=!0)}function ih(){var a;qg(window,"resize",function(){null==a&&(a=setTimeout(function(){a=null,kh=null,fh(dd)},100))}),qg(window,"blur",function(){fh(Id)})}function lh(a){if(null!=kh)return kh;var b=Xg("div",null,null,"width: 50px; height: 50px; overflow-x: scroll");return $g(a,b),b.offsetWidth&&(kh=b.offsetHeight-b.clientHeight),kh||0}function nh(a){if(null==mh){var b=Xg("span","\u200b");$g(a,Xg("span",[b,document.createTextNode("x")])),0!=a.firstChild.offsetHeight&&(mh=b.offsetWidth<=1&&b.offsetHeight>2&&!(d&&8>e))}return mh?Xg("span","\u200b"):Xg("span","\xa0",null,"display: inline-block; width: 1px; margin-right: -1px")}function ph(a){if(null!=oh)return oh;var b=$g(a,document.createTextNode("A\u062eA")),c=Yg(b,0,1).getBoundingClientRect();if(!c||c.left==c.right)return!1;var d=Yg(b,1,2).getBoundingClientRect();return oh=d.right-c.right<3}function uh(a){if(null!=th)return th;var b=$g(a,Xg("span","x")),c=b.getBoundingClientRect(),d=Yg(b,0,1).getBound
ingClientRect();return th=Math.abs(c.left-d.left)>1}function wh(a,b,c,d){if(!a)return d(b,c,"ltr");for(var e=!1,f=0;f<a.length;++f){var g=a[f];(g.from<c&&g.to>b||b==c&&g.to==b)&&(d(Math.max(g.from,b),Math.min(g.to,c),1==g.level?"rtl":"ltr"),e=!0)}e||d(b,c,"ltr")}function xh(a){return a.level%2?a.to:a.from}function yh(a){return a.level%2?a.from:a.to}function zh(a){var b=Wf(a);return b?xh(b[0]):0}function Ah(a){var b=Wf(a);return b?yh(Kg(b)):a.text.length}function Bh(a,b){var c=Pf(a.doc,b),d=af(c);d!=c&&(b=Tf(d));var e=Wf(d),f=e?e[0].level%2?Ah(d):zh(d):0;return mb(b,f)}function Ch(a,b){for(var c,d=Pf(a.doc,b);c=$e(d);)d=c.find(1,!0).line,b=null;var e=Wf(d),f=e?e[0].level%2?zh(d):Ah(d):d.text.length;return mb(null==b?Tf(d):b,f)}function Dh(a,b){var c=Bh(a,b.line),d=Pf(a.doc,c.line),e=Wf(d);if(!e||0==e[0].level){var f=Math.max(0,d.text.search(/\S/)),g=b.line==c.line&&b.ch<=f&&b.ch;return mb(c.line,g?0:f)}return c}function Eh(a,b,c){var d=a[0].level;return b==d?!0:c==d?!1:c>b}function G
h(a,b){Fh=null;for(var d,c=0;c<a.length;++c){var e=a[c];if(e.from<b&&e.to>b)return c;if(e.from==b||e.to==b){if(null!=d)return Eh(a,e.level,a[d].level)?(e.from!=e.to&&(Fh=d),c):(e.from!=e.to&&(Fh=c),d);d=c}}return d}function Hh(a,b,c,d){if(!d)return b+c;do b+=c;while(b>0&&Wg(a.text.charAt(b)));return b}function Ih(a,b,c,d){var e=Wf(a);if(!e)return Jh(a,b,c,d);for(var f=Gh(e,b),g=e[f],h=Hh(a,b,g.level%2?-c:c,d);;){if(h>g.from&&h<g.to)return h;if(h==g.from||h==g.to)return Gh(e,h)==f?h:(g=e
<TRUNCATED>
[05/12] ambari git commit: AMBARI-9676. Initial Hive View Submission
to Contrib (alexantonenko)
Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs
new file mode 100644
index 0000000..a870a0e
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-delete.hbs
@@ -0,0 +1,21 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+{{#modal-widget heading=heading close="close" ok="delete"}}
+ {{text}}
+{{/modal-widget}}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-save.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-save.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-save.hbs
new file mode 100644
index 0000000..f327e3b
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/modal-save.hbs
@@ -0,0 +1,21 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+{{#modal-widget heading=heading close="close" ok="save"}}
+ {{input type="text" class="form-control" value=text }}
+{{/modal-widget}}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/navbar.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/navbar.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/navbar.hbs
new file mode 100644
index 0000000..f8f6bcb
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/navbar.hbs
@@ -0,0 +1,45 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<nav class="navbar navbar-default no-margin" role="navigation">
+ <div class="container-fluid">
+ <!-- Brand and toggle get grouped for better mobile display -->
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ {{#link-to "index" classNames="navbar-brand"}}
+ {{view.title}}
+ {{/link-to}}
+ </div>
+
+ <!-- Collect the nav links, forms, and other content for toggling -->
+ <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
+ <ul class="nav navbar-nav">
+ {{#each item in view.items}}
+ {{#link-to item.path tagName="li"}}
+ <a>{{tb-helper "text" item}}</a>
+ {{/link-to}}
+ {{/each}}
+ </ul>
+ </div>
+ </div>
+</nav>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs
new file mode 100644
index 0000000..ad1d9bd
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/open-queries.hbs
@@ -0,0 +1,23 @@
+{{!
+* 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.
+}}
+
+{{#tabs-widget tabs=queryTabs removeClicked="removeQueryTab" canRemove=true}}
+ {{render 'settings'}}
+ {{query-editor tables=selectedTables query=currentQuery.fileContent editor=view.editor highlightedText=highlightedText
+ columnsNeeded="getColumnsForAutocomplete"}}
+{{/tabs-widget}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/queries.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/queries.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/queries.hbs
new file mode 100644
index 0000000..6187dfe
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/queries.hbs
@@ -0,0 +1,78 @@
+{{!
+* 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.
+}}
+
+<table class="table">
+ <thead>
+ <tr>
+ {{#each column in columns}}
+ <th>
+ {{#if column.caption}}
+ {{column-filter-widget class="pull-left"
+ column=column
+ filterValue=column.filterValue
+ sortAscending=controller.sortAscending
+ sortProperties=controller.sortProperties
+ columnSorted="sort"
+ columnFiltered="filter"}}
+ {{else}}
+ {{column.caption}}
+ {{/if}}
+ </th>
+ {{/each}}
+ <th>
+ <button type="btn" class="btn btn-sm btn-warning pull-right clear-filters" {{action "clearFilters"}}>{{t "buttons.clearFilters"}}</button>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ {{#each query in this}}
+ <tr>
+ <td>
+ {{#link-to "index.savedQuery" query}}
+ {{query.shortQuery}}
+ {{/link-to}}
+ </td>
+
+ <td>
+ {{#link-to "index.savedQuery" query}}
+ {{query.title}}
+ {{/link-to}}
+ </td>
+
+ <td>{{query.dataBase}}</td>
+
+ <td>{{query.owner}}</td>
+
+ <td>
+ {{#unless query.isNew}}
+ <div class="btn-group pull-right">
+ <span data-toggle="dropdown">
+ <a class="fa fa-gear"></a>
+ </span>
+ <ul class="dropdown-menu" role="menu">
+ {{#each link in controller.links}}
+ <li {{action 'executeAction' link query}}><a>{{tb-helper link}}</a></li>
+ {{/each}}
+ </ul>
+ </div>
+ {{/unless}}
+ </td>
+ </tr>
+ {{/each}}
+ </tbody>
+</table>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/redirect.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/redirect.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/redirect.hbs
new file mode 100644
index 0000000..b776fd2
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/redirect.hbs
@@ -0,0 +1,19 @@
+{{!
+* 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.
+}}
+
+{{outlet}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs
new file mode 100644
index 0000000..15741802
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/settings.hbs
@@ -0,0 +1,52 @@
+{{!
+* 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.
+}}
+
+<span {{bind-attr class="showSettingsOverlay:active :fa :fa-gear :settings-toggle"}} {{action 'toggleOverlay'}}></span>
+
+{{#if showSettingsOverlay}}
+<div class="settings-container fadeIn">
+ <h3>Settings
+ <button class="btn btn-success btn-xs" {{action 'add'}}><i class="fa fa-plus"></i> Add</button>
+
+ <div class="checkbox btn btn-primary btn-xs runOnTez">
+ <label for="runOnTez">
+ {{input type="checkbox" checked=querySettings.runOnTez id="runOnTez"}} {{t "buttons.runOnTez"}}
+ </label>
+ </div>
+ </h3>
+
+ {{#each setting in querySettings.settings}}
+ <div class="setting">
+ <form class="form-inline">
+ <div class="form-group">
+ <div class="input-group">
+ <div class="input-group-addon">
+ {{input class="input-sm form-control" placeholder="Key" value=setting.key}}
+ </div>
+ <div class="input-group-addon">
+ {{input class="input-sm form-control" placeholder="Value" value=setting.value}}
+ <span class="fa fa-times-circle remove" {{action 'remove' setting}}></span>
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+ {{/each}}
+
+</div>
+{{/if}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/udfs.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/udfs.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/udfs.hbs
new file mode 100644
index 0000000..5bb8759
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/udfs.hbs
@@ -0,0 +1,112 @@
+{{!
+* 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.
+}}
+
+<table class="table">
+ <thead>
+ <tr>
+ <th>{{t "columns.fileResource"}}</th>
+ {{#each column in columns}}
+ <th>
+ {{#if column.caption}}
+ {{column-filter-widget class="pull-left"
+ column=column
+ filterValue=column.filterValue
+ sortAscending=controller.sortAscending
+ sortProperties=controller.sortProperties
+ columnSorted="sort"
+ columnFiltered="filter"}}
+ {{else}}
+ {{column.caption}}
+ {{/if}}
+ </th>
+ {{/each}}
+ <th>
+ <div class="pull-right">
+ <button type="button" class="btn btn-sm btn-warning clear-filters" {{action "clearFilters"}}>{{t "buttons.clearFilters"}}</button>
+ <button type="button" class="btn btn-sm btn-success add-udf" {{action "add"}}>{{t "buttons.newUdf"}}</button>
+ </div>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ {{#each udf in this}}
+ <tr>
+ <td>
+ {{#if udf.isEditing}}
+ {{#if udf.isEditingResource}}
+ {{extended-input type="text"
+ class="pull-left form-control halfed input-sm"
+ placeholderTranslation="placeholders.fileResource.name"
+ value=udf.fileResource.name}}
+ {{extended-input type="text"
+ class="pull-left form-control halfed input-sm"
+ placeholderTranslation="placeholders.fileResource.path"
+ value=udf.fileResource.path}}
+ {{else}}
+ {{select-widget items=controller.fileResources
+ selectedValue=udf.fileResource
+ labelPath="name"
+ defaultLabelTranslation="placeholders.select.file"
+ itemAdded="addFileResource"
+ itemEdited="editFileResource"
+ itemRemoved="removeFileResource"
+ canAdd=true
+ canEdit=true}}
+ {{/if}}
+ {{else}}
+ {{#if udf.fileResource}}
+ {{udf.fileResource.name}} ({{udf.fileResource.path}})
+ {{/if}}
+ {{/if}}
+ </td>
+ {{#each column in controller.columns}}
+ <td>
+ {{#if udf.isEditing}}
+ {{extended-input type="text"
+ class="pull-left form-control input-sm"
+ placeholderTranslation=column.caption
+ dynamicContextBinding="udf"
+ dynamicValueBinding="column.property"}}
+ {{else}}
+ {{path-binding udf column.property}}
+ {{/if}}
+ </td>
+ {{/each}}
+ <td>
+ {{#if udf.isEditing}}
+ <div class="pull-right">
+ <button type="button" class="btn btn-sm btn-warning" {{action "cancel"}}>{{t "buttons.cancel"}}</button>
+ <button type="button" class="btn btn-sm btn-success" {{action "save"}}>{{t "buttons.save"}}</button>
+ </div>
+ {{else}}
+ <div class="btn-group pull-right">
+ <span data-toggle="dropdown">
+ <a class="fa fa-gear"></a>
+ </span>
+ <ul class="dropdown-menu" role="menu">
+ {{#each link in controller.links}}
+ <li {{action 'executeAction' link}}><a>{{tb-helper link}}</a></li>
+ {{/each}}
+ </ul>
+ </div>
+ {{/if}}
+ </td>
+ </tr>
+ {{/each}}
+ </tbody>
+</table>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/transforms/date.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/transforms/date.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/transforms/date.js
new file mode 100644
index 0000000..716ab84
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/transforms/date.js
@@ -0,0 +1,49 @@
+/**
+ * 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 Ember from 'ember';
+import DS from 'ember-data';
+
+export default DS.Transform.extend({
+
+ deserialize: function (serialized) {
+ var type = typeof serialized;
+
+ if (type === "string") {
+ return new Date(Ember.Date.parse(serialized));
+ } else if (type === "number") {
+ return new Date(serialized);
+ } else if (serialized === null || serialized === undefined) {
+ // if the value is not present in the data,
+ // return undefined, not null.
+ return serialized;
+ } else {
+ return null;
+ }
+ },
+
+ serialize: function (date) {
+ if (date instanceof Date) {
+ // Serialize it as a number to maintain millisecond precision
+ return Number(date);
+ } else {
+ return null;
+ }
+ }
+
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js
new file mode 100644
index 0000000..c9b1fda
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/constants.js
@@ -0,0 +1,122 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Object.create({
+ appTitle: 'Hive',
+
+ /**
+ * This should reflect the naming conventions accross the application.
+ * Changing one value also means changing the filenames for the chain of files
+ * represented by that value (routes, controllers, models etc).
+ * This dependency goes both ways.
+ */
+ namingConventions: {
+ routes: {
+ index: 'index',
+ savedQuery: 'savedQuery',
+ historyQuery: 'historyQuery',
+ queries: 'queries',
+ history: 'history',
+ udfs: 'udfs',
+ logs: 'logs',
+ results: 'results',
+ explain: 'explain'
+ },
+
+ subroutes: {
+ savedQuery: 'index.savedQuery',
+ historyQuery: 'index.historyQuery',
+ jobLogs: 'index.historyQuery.logs',
+ jobResults: 'index.historyQuery.results',
+ jobExplain: 'index.historyQuery.explain'
+ },
+
+ index: 'index',
+ udf: 'udf',
+ udfs: 'udfs',
+ udfInsertPrefix: 'create temporary function ',
+ fileInsertPrefix: 'add jar ',
+ explainPrefix: 'EXPLAIN ',
+ insertUdfs: 'insert-udfs',
+ job: 'job',
+ jobs: 'jobs',
+ history: 'history',
+ savedQuery: 'saved-query',
+ database: 'database',
+ databases: 'databases',
+ openQueries: 'open-queries',
+ file: 'file',
+ fileResource: 'file-resource',
+ fileResources: 'file-resources',
+ loadedFiles: 'loaded-files',
+ alerts: 'alerts',
+ logs: 'logs',
+ results: 'results',
+ jobResults: 'index/history-query/results',
+ jobLogs: 'index/history-query/logs',
+ jobExplain: 'index/history-query/explain',
+ databaseTree: 'databases-tree',
+ databaseSearch: 'databases-search-results',
+ tables: 'tables',
+ columns: 'columns',
+ settings: 'settings'
+ },
+
+ statuses: {
+ unknown: "Unknown",
+ initialized: "Initialized",
+ running: "Running",
+ finished: "Finished",
+ canceled: "Canceled",
+ closed: "Closed",
+ error: "Error",
+ pending: "Pending"
+ },
+
+ alerts: {
+ warning: 'warning',
+ error: 'danger',
+ success: 'success'
+ },
+
+ results: {
+ save: {
+ csv: 'Save as csv',
+ hdfs: 'Save to HDFS'
+ },
+ statuses: {
+ terminated: "TERMINATED",
+ runnable: "RUNNABLE"
+ }
+ },
+
+ //this can be replaced by a string.format implementation
+ adapter: {
+ version: '0.0.1',
+ instance: 'Hive',
+ apiPrefix: '/api/v1/views/HIVE/versions/',
+ instancePrefix: '/instances/',
+ resourcePrefix: 'resources/'
+ },
+
+ settings: {
+ executionEngine: 'hive.execution.engine'
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/functions.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/functions.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/functions.js
new file mode 100644
index 0000000..ab4f8e5
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/utils/functions.js
@@ -0,0 +1,36 @@
+/**
+ * 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 Ember from 'ember';
+
+/* globals moment */
+
+export default Ember.Object.create({
+ isInteger: function (x) {
+ return (x^0) === x;
+ },
+
+ isDate: function(date) {
+ return moment(date).isValid();
+ },
+
+ regexes: {
+ allUppercase: /^[^a-z]*$/,
+ whitespaces: /^(\s*).*$/
+ }
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/views/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/views/.gitkeep b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/.gitkeep
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/views/history.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/views/history.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/history.js
new file mode 100644
index 0000000..8a914f0
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/history.js
@@ -0,0 +1,25 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.View.extend({
+ colspan: function () {
+ return this.get('controller.columns.length') + 1;
+ }.property('controller.columns')
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/views/insert-udfs.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/views/insert-udfs.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/insert-udfs.js
new file mode 100644
index 0000000..70ed640
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/insert-udfs.js
@@ -0,0 +1,23 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.View.extend({
+ tagName: 'insert-udfs'
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/views/navbar.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/views/navbar.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/navbar.js
new file mode 100644
index 0000000..0c535db
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/views/navbar.js
@@ -0,0 +1,38 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.View.extend({
+ title: constants.appTitle,
+
+ items: Ember.A([
+ Ember.Object.create({text: 'menus.query',
+ path: constants.namingConventions.routes.index}),
+
+ Ember.Object.create({text: 'menus.savedQueries',
+ path: constants.namingConventions.routes.queries}),
+
+ Ember.Object.create({text: 'menus.history',
+ path: constants.namingConventions.routes.history}),
+
+ Ember.Object.create({text: 'menus.udfs',
+ path: constants.namingConventions.routes.udfs})
+ ])
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/big_tables.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/big_tables.js b/contrib/views/hive/src/main/resources/ui/hive-web/big_tables.js
new file mode 100644
index 0000000..9f3a317
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/big_tables.js
@@ -0,0 +1,54 @@
+/**
+ * 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 result = '';
+var tableCount = 15000;
+var columnCount = 100;
+
+//tables and columns script
+for (var i = 0; i < tableCount; i++) {
+ result += 'CREATE TABLE TABLE_' + i + ' (';
+ (function () {
+ for (var j = 0; j < columnCount; j++) {
+ result += 'field_' + j + ' STRING';
+
+ if (j < columnCount - 1) {
+ result += ',';
+ } else {
+ result += ') '
+ }
+ }
+ }());
+
+ result += "ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' STORED AS TEXTFILE; \nLOAD DATA LOCAL INPATH 'test.csv' OVERWRITE INTO TABLE " +
+ 'TABLE_' + i + ';\n\n';
+}
+
+console.log(result);
+
+//csv script
+var fill = '';
+for (var i = 0; i < columnCount; i++) {
+ fill += 'field_' + i;
+
+ if (i < columnCount - 1) {
+ fill += ', ';
+ }
+}
+
+console.log(fill);
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/bower.json
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/bower.json b/contrib/views/hive/src/main/resources/ui/hive-web/bower.json
new file mode 100644
index 0000000..a4c2aca
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/bower.json
@@ -0,0 +1,27 @@
+{
+ "name": "hive",
+ "dependencies": {
+ "handlebars": "2.0.0",
+ "jquery": "^1.11.1",
+ "ember": "1.9.0",
+ "ember-data": "1.0.0-beta.11",
+ "ember-resolver": "~0.1.7",
+ "loader.js": "stefanpenner/loader.js#1.0.1",
+ "ember-cli-shims": "stefanpenner/ember-cli-shims#0.0.3",
+ "ember-cli-test-loader": "rwjblue/ember-cli-test-loader#0.0.4",
+ "ember-load-initializers": "stefanpenner/ember-load-initializers#0.0.2",
+ "ember-qunit": "0.1.8",
+ "ember-qunit-notifications": "0.0.4",
+ "qunit": "~1.15.0",
+ "bootstrap": "~3.2.0",
+ "ember-i18n": "~2.9.0",
+ "blanket": "~1.1.5",
+ "jquery-ui": "~1.11.2",
+ "selectize": "~0.11.2",
+ "pretender": "0.1.0"
+ },
+ "resolutions": {
+ "handlebars": "2.0.0",
+ "ember": "1.9.0"
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/config/environment.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/config/environment.js b/contrib/views/hive/src/main/resources/ui/hive-web/config/environment.js
new file mode 100644
index 0000000..86da93e
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/config/environment.js
@@ -0,0 +1,65 @@
+/**
+ * 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.
+ */
+
+/* jshint node: true */
+
+module.exports = function(environment) {
+ var ENV = {
+ modulePrefix: 'hive',
+ environment: environment,
+ baseURL: '/',
+ locationType: 'hash',
+ EmberENV: {
+ FEATURES: {
+ // Here you can enable experimental features on an ember canary build
+ // e.g. 'with-controller': true
+ }
+ },
+
+ APP: {
+ // Here you can pass flags/options to your application instance
+ // when it is created
+ }
+ };
+
+ if (environment === 'development') {
+ // ENV.APP.LOG_RESOLVER = true;
+ ENV.APP.LOG_ACTIVE_GENERATION = true;
+ // ENV.APP.LOG_TRANSITIONS = true;
+ // ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
+ ENV.APP.LOG_VIEW_LOOKUPS = true;
+ }
+
+ if (environment === 'test') {
+ // Testem prefers this...
+ ENV.baseURL = '/';
+ ENV.locationType = 'auto';
+
+ // keep test console output quieter
+ ENV.APP.LOG_ACTIVE_GENERATION = false;
+ ENV.APP.LOG_VIEW_LOOKUPS = false;
+
+ ENV.APP.rootElement = '#ember-testing';
+ }
+
+ if (environment === 'production') {
+
+ }
+
+ return ENV;
+};
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/package.json
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/package.json b/contrib/views/hive/src/main/resources/ui/hive-web/package.json
new file mode 100644
index 0000000..52e908a
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "hive",
+ "version": "0.0.0",
+ "private": true,
+ "directories": {
+ "doc": "doc",
+ "test": "tests"
+ },
+ "scripts": {
+ "start": "ember server",
+ "build": "ember build",
+ "test": "ember test",
+ "preinstall": "chmod +x node/npm/bin/node-gyp-bin/node-gyp",
+ "postinstall": "bash node/with_new_path.sh node node_modules/.bin/bower --allow-root install"
+ },
+ "repository": "https://github.com/stefanpenner/ember-cli",
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "author": "",
+ "license": "MIT",
+ "devDependencies": {
+ "body-parser": "^1.2.0",
+ "bower": ">= 1.3.12",
+ "broccoli-asset-rev": "0.3.1",
+ "broccoli-sass": "^0.3.2",
+ "ember-cli": "0.1.2",
+ "ember-cli-blanket": "^0.2.2",
+ "ember-cli-content-security-policy": "0.3.0",
+ "ember-cli-font-awesome": "0.0.4",
+ "ember-cli-htmlbars": "^0.5.3",
+ "ember-cli-ic-ajax": "0.1.1",
+ "ember-cli-inject-live-reload": "^1.3.0",
+ "ember-cli-jquery-ui": "0.0.12",
+ "ember-cli-moment": "0.0.1",
+ "ember-cli-pretender": "^0.3.1",
+ "ember-cli-qunit": "0.1.0",
+ "ember-cli-selectize": "0.0.7",
+ "ember-data": "1.0.0-beta.10",
+ "ember-dynamic-component": "0.0.1",
+ "ember-export-application-global": "^1.0.0",
+ "express": "^4.8.5",
+ "glob": "^4.0.5"
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/testem.json
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/testem.json b/contrib/views/hive/src/main/resources/ui/hive-web/testem.json
new file mode 100644
index 0000000..eff93f9
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/testem.json
@@ -0,0 +1,11 @@
+{
+ "framework": "qunit",
+ "test_page": "tests/index.html",
+ "launch_in_ci": [
+ "PhantomJS"
+ ],
+ "launch_in_dev": [
+ "PhantomJS",
+ "Chrome"
+ ]
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/.jshintrc
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/.jshintrc b/contrib/views/hive/src/main/resources/ui/hive-web/tests/.jshintrc
new file mode 100644
index 0000000..6ebf71a
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/.jshintrc
@@ -0,0 +1,74 @@
+{
+ "predef": [
+ "document",
+ "window",
+ "location",
+ "setTimeout",
+ "$",
+ "-Promise",
+ "QUnit",
+ "define",
+ "console",
+ "equal",
+ "notEqual",
+ "notStrictEqual",
+ "test",
+ "asyncTest",
+ "testBoth",
+ "testWithDefault",
+ "raises",
+ "throws",
+ "deepEqual",
+ "start",
+ "stop",
+ "ok",
+ "strictEqual",
+ "module",
+ "moduleFor",
+ "moduleForComponent",
+ "moduleForModel",
+ "process",
+ "expect",
+ "visit",
+ "exists",
+ "fillIn",
+ "click",
+ "keyEvent",
+ "triggerEvent",
+ "find",
+ "findWithAssert",
+ "wait",
+ "DS",
+ "isolatedContainer",
+ "startApp",
+ "andThen",
+ "currentURL",
+ "currentPath",
+ "currentRouteName"
+ ],
+ "node": false,
+ "browser": false,
+ "boss": true,
+ "curly": false,
+ "debug": false,
+ "devel": false,
+ "eqeqeq": true,
+ "evil": true,
+ "forin": false,
+ "immed": false,
+ "laxbreak": false,
+ "newcap": true,
+ "noarg": true,
+ "noempty": false,
+ "nonew": false,
+ "nomen": false,
+ "onevar": false,
+ "plusplus": false,
+ "regexp": false,
+ "undef": true,
+ "sub": true,
+ "strict": false,
+ "white": false,
+ "eqnull": true,
+ "esnext": true
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/blanket-options.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/blanket-options.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/blanket-options.js
new file mode 100644
index 0000000..a5580a9
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/blanket-options.js
@@ -0,0 +1,25 @@
+/**
+ * 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.
+ */
+
+/* globals blanket */
+
+blanket.options({
+ filter: "//.*hive/.*/",
+ antifilter: ["//.*(tests).*/", "//.*(templates).*/"],
+ loaderExclusions: ['ember-cli-jquery-ui']
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js
new file mode 100644
index 0000000..46a73f9
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/api-mock.js
@@ -0,0 +1,242 @@
+/**
+ * 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 applicationAdapter from 'hive/adapters/database';
+
+export default function() {
+ var baseUrl = applicationAdapter.create().buildURL();
+ var databases = ['db1', 'db2', 'db3'];
+
+ this.get(baseUrl + '/resources/ddl/database', function(req) {
+ var db = {
+ databases: databases
+ };
+
+ return [200, {"Content-Type": "application/json"}, JSON.stringify(db)];
+ });
+
+ this.get(baseUrl + '/resources/ddl/database/db1/table.page', function (req) {
+ var tables = {
+ rows: [
+ ['table1'],
+ ['table2'],
+ ['table3']
+ ]
+ }
+
+ return [200, {"Content-Type": "application/json"}, JSON.stringify(tables)];
+ });
+
+ this.get(baseUrl + '/resources/ddl/database/db1/table/table1.page', function (req) {
+ var columns = {
+ rows: [
+ ['column1', 'STRING'],
+ ['column2', 'STRING'],
+ ['column3', 'STRING']
+ ]
+ }
+
+ return [200, {"Content-Type": "application/json"}, JSON.stringify(columns)];
+ });
+
+ this.get(baseUrl + '/udfs', function(req) {
+ var udf = {
+ "udfs": [{
+ "name": "TestColumn",
+ "classname": "TestClassName",
+ "fileResource": 1,
+ "id": 1,
+ "owner": "owner1"
+ },
+ {
+ "name": "Test2Columns",
+ "classname": "Test2ClassName",
+ "fileResource": 1,
+ "id": 2,
+ "owner": "owner2"
+ }]
+ };
+
+ return [200, {"Content-Type": "application/json"}, JSON.stringify(udf)];
+ });
+
+ this.post(baseUrl + '/jobs', function(req) {
+ var job = {
+ "job": {
+ "status":"Finished",
+ "dataBase":"db1",
+ "dateSubmitted":1421677418,
+ "logFile":"job1/logs",
+ "properties":{},
+ "fileResources":[],
+ "statusDir":"job1",
+ "id":1,
+ "title":"New Query",
+ "duration":2,
+ "forcedContent":"",
+ "owner":"admin",
+ "confFile":"job1/conf",
+ "queryId":null,
+ "queryFile":"job1.hql"
+ }
+ };
+
+ return [200, {"Content-Type": "application/json"}, JSON.stringify(job)];
+ });
+
+ this.get(baseUrl + '/resources/file/job1.hql', function(req) {
+ var file = {
+ "file": {
+ "filePath": "job1.hql",
+ "fileContent": "select * from big",
+ "hasNext": false,
+ "page": 0,
+ "pageCount": 1
+ }
+ };
+
+ return [200, {"Content-Type": "application/json"}, JSON.stringify(file)];
+ });
+
+ this.get(baseUrl + '/savedQueries', function(req) {
+ var savedQueries = {
+ "savedQueries": [{
+ "queryFile": "saved1.hql",
+ "dataBase": "db1",
+ "title": "saved1",
+ "shortQuery": "",
+ "id": 1,
+ "owner": "owner1"
+ }, {
+ "queryFile": "saved2.hql",
+ "dataBase": "db2",
+ "title": "saved2",
+ "shortQuery": "select count(field_0) from big;",
+ "id": 2,
+ "owner": "owner2"
+ }]
+ };
+
+ return [200, {"Content-Type": "application/json"}, JSON.stringify(savedQueries)];
+ });
+
+ this.get(baseUrl + '/resources/file/saved1.hql', function(req) {
+ var file = {
+ "file": {
+ "filePath": "saved1.hql",
+ "fileContent": "select * from saved1",
+ "hasNext": false,
+ "page": 0,
+ "pageCount": 0
+ }
+ };
+
+ return [200, {"Content-Type": "application/json"}, JSON.stringify(file)];
+ });
+
+ this.get(baseUrl + '/jobs', function(req) {
+ var jobs = {
+ "jobs": [
+ {
+ "title": "Query1",
+ "queryFile": "saved1.hql",
+ "statusDir": "statusdir",
+ "dateSubmitted": 1421240048,
+ "duration": 97199,
+ "status": "Finished",
+ "forcedContent": "",
+ "id": 1,
+ "owner": "admin",
+ "logFile": "logs1",
+ "confFile": "conf1"
+ },
+ {
+ "title": "Query2",
+ "queryFile": "saved1.hql",
+ "statusDir": "statusdir",
+ "dateSubmitted": 1421240048,
+ "duration": 97199,
+ "status": "Finished",
+ "forcedContent": "",
+ "id": 2,
+ "owner": "admin",
+ "logFile": "logs2",
+ "confFile": "conf2"
+ },
+ {
+ "title": "Query3",
+ "queryFile": "saved1.hql",
+ "statusDir": "statusdir",
+ "dateSubmitted": 1421240048,
+ "duration": 97199,
+ "status": "Running",
+ "forcedContent": "",
+ "id": 3,
+ "owner": "admin",
+ "logFile": "logs3",
+ "confFile": "conf3"
+ },
+ {
+ "title": "Query4",
+ "queryFile": "saved1.hql",
+ "statusDir": "statusdir",
+ "dateSubmitted": 1421240048,
+ "duration": 97199,
+ "status": "Error",
+ "forcedContent": "",
+ "id": 4,
+ "owner": "admin",
+ "logFile": "logs4",
+ "confFile": "con51"
+ }
+ ]
+ };
+
+ return [200, {"Content-Type": "application/json"}, JSON.stringify(jobs)];
+ });
+
+ this.get(baseUrl + '/fileResources', function(req) {
+ var files = {
+ "fileResources": [
+ {
+ "name": "TestName",
+ "path": "TestPath",
+ "id": 1,
+ "owner": "owner1"
+ }
+ ]
+ };
+
+ return [200, {"Content-Type": "application/json"}, JSON.stringify(files)];
+ });
+
+ this.get(baseUrl + '/fileResources/1', function(req) {
+ var files = {
+ "fileResources": [
+ {
+ "name": "TestName",
+ "path": "TestPath",
+ "id": 1,
+ "owner": "owner1"
+ }
+ ]
+ };
+
+ return [200, {"Content-Type": "application/json"}, JSON.stringify(files)];
+ });
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/resolver.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/resolver.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/resolver.js
new file mode 100644
index 0000000..f94998c
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/resolver.js
@@ -0,0 +1,29 @@
+/**
+ * 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 Resolver from 'ember/resolver';
+import config from '../../config/environment';
+
+var resolver = Resolver.create();
+
+resolver.namespace = {
+ modulePrefix: config.modulePrefix,
+ podModulePrefix: config.podModulePrefix
+};
+
+export default resolver;
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/start-app.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/start-app.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/start-app.js
new file mode 100644
index 0000000..e029055
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/helpers/start-app.js
@@ -0,0 +1,43 @@
+/**
+ * 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 Ember from 'ember';
+import Application from '../../app';
+import Router from '../../router';
+import config from '../../config/environment';
+
+export default function startApp(attrs) {
+ var App;
+
+ var attributes = Ember.merge({}, config.APP);
+ attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
+
+ Router.reopen({
+ location: 'none'
+ });
+
+ Ember.run(function() {
+ App = Application.create(attributes);
+ App.setupForTesting();
+ App.injectTestHelpers();
+ });
+
+ App.reset(); // this shouldn't be needed, i want to be able to "start an app at a specific URL"
+
+ return App;
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/img/spinner.gif
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/img/spinner.gif b/contrib/views/hive/src/main/resources/ui/hive-web/tests/img/spinner.gif
new file mode 100644
index 0000000..39fcb67
Binary files /dev/null and b/contrib/views/hive/src/main/resources/ui/hive-web/tests/img/spinner.gif differ
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html b/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html
new file mode 100644
index 0000000..6adbc89
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/index.html
@@ -0,0 +1,64 @@
+<!--
+ * 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.
+-->
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <title>Hive Tests</title>
+ <meta name="description" content="">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+
+ {{content-for 'head'}}
+ {{content-for 'test-head'}}
+
+ <link rel="stylesheet" href="assets/vendor.css">
+ <link rel="stylesheet" href="assets/hive.css">
+ <link rel="stylesheet" href="assets/test-support.css">
+ <style>
+ #ember-testing-container {
+ position: absolute;
+ background: white;
+ bottom: 0;
+ right: 0;
+ width: 640px;
+ height: 384px;
+ overflow: auto;
+ z-index: 9999;
+ border: 1px solid #ccc;
+ }
+ #ember-testing {
+ zoom: 50%;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="qunit"></div>
+ <div id="qunit-fixture"></div>
+
+ {{content-for 'body'}}
+ {{content-for 'test-body'}}
+ <script src="assets/vendor.js"></script>
+ <script src="assets/test-support.js"></script>
+ <script src="assets/hive.js"></script>
+ <script src="assets/blanket-options.js"></script>
+ <script src="assets/blanket-loader.js"></script>
+ <script src="testem.js"></script>
+ <script src="assets/test-loader.js"></script>
+ </body>
+</html>
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js
new file mode 100644
index 0000000..5be4fdc
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/database-test.js
@@ -0,0 +1,105 @@
+/**
+ * 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 Ember from 'ember';
+import { test } from 'ember-qunit';
+import startApp from '../helpers/start-app';
+import api from '../helpers/api-mock';
+
+var App;
+var server;
+
+module('Integration: Databases', {
+ setup: function() {
+ App = startApp();
+ /* global Pretender: true */
+ server = new Pretender(api);
+ },
+ teardown: function() {
+ Ember.run(App, App.destroy);
+ server.shutdown();
+ }
+});
+
+test('Database Explorer is displayed and populated with databases from server.', function() {
+ expect(2);
+
+ visit('/');
+
+ andThen(function() {
+ equal(find('.database-explorer').length, 1, 'Databases panel is visible.');
+ equal(find('.database-explorer .databases').children().length, 3, 'Databases are listed.');
+ });
+});
+
+test('Expanding a database will retrieve the first page of tables for that database.', function () {
+ expect(1);
+
+ visit('/');
+
+ andThen(function () {
+ var targetDB = find('.fa-database').first();
+
+ click(targetDB);
+
+ andThen(function () {
+ equal(find('.fa-th').length, 3);
+ });
+ });
+});
+
+test('Expanding a table will retrieve the first page of columns for that table.', function () {
+ expect(2);
+
+ visit('/');
+
+ andThen(function () {
+ var targetDB = find('.fa-database').first();
+
+ click(targetDB);
+
+ andThen(function () {
+ var targetTable = find('.fa-th').first();
+
+ click(targetTable);
+
+ andThen(function () {
+ console.log(find('.columns'));
+
+ equal(find('.columns').length, 1, 'Columns container was loaded.');
+ equal(find('.columns strong').length, 3, '3 columns were loaded for selected table.');
+ });
+ });
+ });
+});
+
+test('Searching for a table will display table results and column search field', function () {
+ expect(2);
+
+ visit('/');
+
+ andThen(function () {
+ fillIn(find('input').first(), 'table');
+ keyEvent(find('input').first(), 'keyup', 13);
+
+ andThen(function () {
+ equal(find('input').length, 2, 'Columns search input has been rendered.');
+ equal(find('.nav-tabs li').length, 2, 'Results tab has been redendered.');
+ });
+ });
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/history-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/history-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/history-test.js
new file mode 100644
index 0000000..35a950d
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/history-test.js
@@ -0,0 +1,95 @@
+/**
+ * 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 Ember from 'ember';
+import { test } from 'ember-qunit';
+import startApp from '../helpers/start-app';
+import api from '../helpers/api-mock';
+
+var App;
+var server;
+
+module('Integration: History', {
+ setup: function() {
+ App = startApp();
+ /* global Pretender: true */
+ server = new Pretender(api);
+ },
+
+ teardown: function() {
+ Ember.run(App, App.destroy);
+ server.shutdown();
+ }
+});
+
+test('Save Queries should list saved queries', function() {
+ expect(1);
+
+ visit("/history");
+
+ andThen(function() {
+ equal(find('#content .table tbody tr').length, 4);
+ });
+});
+
+test('User should be able to filter the jobs', function() {
+ expect(4);
+
+ visit("/history");
+
+ fillIn('column-filter input[placeholder=title]', "Query1");
+ keyEvent('column-filter input[placeholder=title]', 'keyup');
+
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 1, 'User is able to filter by title');
+ });
+
+ click('.clear-filters');
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 4);
+ });
+
+
+ fillIn('column-filter input[placeholder=status]', "Finished");
+ keyEvent('column-filter input[placeholder=status]', 'keyup');
+
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 2, 'User is able to filter by status');
+ });
+
+ click('.clear-filters');
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 4);
+ });
+});
+
+test('A query item should expand to show the HQL', function() {
+ expect(3);
+ visit("/history");
+
+ andThen(function() {
+ equal(find('.table-expandable tbody .secondary-row').length, 0, 'All queries are collapsed');
+ });
+
+ click('.table-expandable tbody tr:first-child');
+
+ andThen(function() {
+ equal(find('.table-expandable tbody .secondary-row').length, 1, 'One query is expanded');
+ ok(find('.table-expandable tbody tr:first-child').next().hasClass('secondary-row'), 'Clicked query is expanded');
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/query-editor-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/query-editor-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/query-editor-test.js
new file mode 100644
index 0000000..235a225
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/query-editor-test.js
@@ -0,0 +1,108 @@
+/**
+ * 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 Ember from 'ember';
+import { test } from 'ember-qunit';
+import startApp from '../helpers/start-app';
+import api from '../helpers/api-mock';
+
+var App;
+var server;
+
+module('Integration: Query Editor', {
+ setup: function() {
+ App = startApp();
+ /* global Pretender: true */
+ server = new Pretender(api);
+ },
+
+ teardown: function() {
+ Ember.run(App, App.destroy);
+ server.shutdown();
+ }
+});
+
+test('Query Editor is visible', function() {
+ expect(1);
+
+ visit("/");
+
+ andThen(function() {
+ equal(find('.query-editor-panel').length, 1, 'Query Editor is visible');
+ });
+});
+
+test('Can execute query', function() {
+ expect(1);
+
+ visit("/");
+
+ Ember.run(function() {
+ find('.CodeMirror').get(0).CodeMirror.setValue('select count(*) from table1');
+ });
+
+ click('.execute-query');
+
+ andThen(function() {
+ ok(find('.query-process-results-panel .nav-tabs li:nth-child(2)').hasClass('active'), 'Results tab is visible');
+ });
+});
+
+
+test('Can save query', function() {
+ expect(2);
+
+ visit("/");
+
+ andThen(function() {
+ equal(find('.modal-dialog').length, 0, 'Modal dialog is hidden');
+ });
+
+ Ember.run(function() {
+ find('.CodeMirror').get(0).CodeMirror.setValue('select count(*) from table1');
+ });
+ click('.save-query-as');
+
+ andThen(function() {
+ equal(find('.modal-dialog').length, 1, 'Modal dialog is shown');
+ });
+
+ click('.modal-dialog .modal-footer .btn-danger');
+});
+
+test('Can execute selection', function() {
+ expect(2);
+ visit('/');
+
+ var query1 = "select color from z;",
+ query2 = "select fruit from z;",
+ query = query1 + "\n" + query2,
+ editor;
+
+ Ember.run(function() {
+ editor = find('.CodeMirror').get(0).CodeMirror;
+ editor.setValue(query);
+ editor.setSelection({ line: 1, ch: 0 }, { line: 1, ch: 20 });
+ });
+ click('.execute-query');
+
+ andThen(function() {
+ equal(editor.getValue(), query, 'Editor value didn\'t change');
+ equal(editor.getSelection(), query2, 'Query 2 is selected');
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/saved-queries-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/saved-queries-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/saved-queries-test.js
new file mode 100644
index 0000000..c444523
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/saved-queries-test.js
@@ -0,0 +1,126 @@
+/**
+ * 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 Ember from 'ember';
+import { test } from 'ember-qunit';
+import startApp from '../helpers/start-app';
+import api from '../helpers/api-mock';
+
+var App;
+var server;
+
+module('Integration: Saved Queries', {
+ setup: function() {
+ App = startApp();
+ /* global Pretender: true */
+ server = new Pretender(api);
+ },
+
+ teardown: function() {
+ Ember.run(App, App.destroy);
+ server.shutdown();
+ }
+});
+
+test('Save Queries should list saved queries', function() {
+ expect(1);
+
+ visit("/queries");
+
+
+ andThen(function() {
+ equal(find('#content .table tbody tr').length, 2);
+ });
+});
+
+test('User should be able to filter the queries', function() {
+ expect(8);
+
+ visit("/queries");
+
+ fillIn('column-filter input[placeholder=preview]', "select count");
+ keyEvent('column-filter input[placeholder=preview]', 'keyup');
+
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 1, 'User is able to filter by short query form.');
+ });
+
+ click('.clear-filters');
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 2);
+ });
+
+ fillIn('column-filter input[placeholder=title]', "saved1");
+ keyEvent('column-filter input[placeholder=title]', 'keyup');
+
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 1, 'User is able to filter by title');
+ });
+
+ click('.clear-filters');
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 2);
+ });
+
+ fillIn('column-filter input[placeholder=database]', "db1");
+ keyEvent('column-filter input[placeholder=database]', 'keyup');
+
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 1, 'User is able to filter by database');
+ });
+
+ click('.clear-filters');
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 2);
+ });
+
+ fillIn('column-filter input[placeholder=owner]', "owner1");
+ keyEvent('column-filter input[placeholder=owner]', 'keyup');
+
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 1, 'User is able to filter by owner');
+ });
+
+ click('.clear-filters');
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 2);
+ });
+});
+
+test('User is able to load a query from saved queries', function() {
+ expect(1);
+
+ visit("/queries");
+ click('#content .table tbody tr:first-child td:first-child a');
+
+ andThen(function() {
+ equal(currentURL(), "/queries/1", 'User is redirected');
+ });
+});
+
+test('Saved Query options menu', function() {
+ expect(2);
+
+ visit("/queries");
+ click('.fa-gear');
+
+ andThen(function() {
+ equal(find('.dropdown-menu:visible').length, 1, 'Query menu is visible');
+ equal(find('.dropdown-menu:visible li').length, 2, 'Query menu has 2 options');
+ });
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/udfs-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/udfs-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/udfs-test.js
new file mode 100644
index 0000000..95a0043
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/integration/udfs-test.js
@@ -0,0 +1,91 @@
+/**
+ * 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 Ember from 'ember';
+import { test } from 'ember-qunit';
+import startApp from '../helpers/start-app';
+import api from '../helpers/api-mock';
+
+var App;
+var server;
+
+module('Integration: Udfs', {
+ setup: function() {
+ App = startApp();
+ /* global Pretender: true */
+ server = new Pretender(api);
+ },
+
+ teardown: function() {
+ Ember.run(App, App.destroy);
+ server.shutdown();
+ }
+});
+
+test('Save Queries should list saved queries', function() {
+ expect(1);
+
+ visit("/udfs");
+
+ andThen(function() {
+ equal(find('#content .table tbody tr').length, 2);
+ });
+});
+
+test('User should be able to filter the udfs', function() {
+ expect(4);
+
+ visit("/udfs");
+
+ fillIn('column-filter input[placeholder="udf name"]', "TestColumn");
+ keyEvent('column-filter input[placeholder="udf name"]', 'keyup');
+
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 1, 'User is able to filter by name');
+ });
+
+ click('.clear-filters');
+
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 2);
+ });
+
+ fillIn('column-filter input[placeholder="udf class name"]', "TestClassName");
+ keyEvent('column-filter input[placeholder="udf class name"]', 'keyup');
+
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 1, 'User is able to filter by class name');
+ });
+
+ click('.clear-filters');
+
+ andThen(function() {
+ equal(find('#content .table tbody tr:visible').length, 2);
+ });
+});
+
+test('User is able to add udf', function() {
+ expect(1);
+
+ visit("/udfs");
+ click('.add-udf');
+
+ andThen(function() {
+ equal(find('#content .table tbody tr').length, 3);
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js
new file mode 100644
index 0000000..53d9f9b
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/test-helper.js
@@ -0,0 +1,30 @@
+/**
+ * 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 resolver from './helpers/resolver';
+import {
+ setResolver
+} from 'ember-qunit';
+
+setResolver(resolver);
+
+document.write('<div id="ember-testing-container"><div id="ember-testing"></div></div>');
+
+QUnit.config.urlConfig.push({ id: 'nocontainer', label: 'Hide container'});
+var containerVisibility = QUnit.urlParams.nocontainer ? 'hidden' : 'visible';
+document.getElementById('ember-testing-container').style.visibility = containerVisibility;
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/.gitkeep b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/.gitkeep
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/adapters/application.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/adapters/application.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/adapters/application.js
new file mode 100644
index 0000000..da39a39
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/adapters/application.js
@@ -0,0 +1,48 @@
+/**
+ * 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 {
+ moduleFor,
+ test
+} from 'ember-qunit';
+
+import constants from 'hive/utils/constants';
+
+moduleFor('adapter:application', 'ApplicationAdapter', {
+ // Specify the other units that are required for this test.
+ // needs: ['serializer:foo']
+});
+
+// Replace this with your real tests.
+test('X-Requested-By header is set.', function() {
+ expect(1);
+
+ var adapter = this.subject();
+
+ ok(adapter.get('headers.X-Requested-By'), 'X-Requested-By is set to a truthy value.');
+});
+
+test('buildUrl returns an url with default values for version and instance paramters if not running within an Ambari instance.', function () {
+ expect(1);
+
+ var adapter = this.subject();
+
+ var url = adapter.buildURL();
+
+ equal(url, constants.adapter.apiPrefix + '0.0.1' + constants.adapter.instancePrefix + 'Hive');
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/adapters/file.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/adapters/file.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/adapters/file.js
new file mode 100644
index 0000000..ea70232
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/adapters/file.js
@@ -0,0 +1,39 @@
+/**
+ * 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 {
+ moduleFor,
+ test
+} from 'ember-qunit';
+
+import constants from 'hive/utils/constants';
+
+moduleFor('adapter:file', 'FileAdapter', {
+ // Specify the other units that are required for this test.
+ // needs: ['serializer:foo']
+});
+
+// Replace this with your real tests.
+test('pathForType returns correct path.', function() {
+ expect(1);
+
+ var adapter = this.subject();
+ var type = 'dummy';
+
+ equal(adapter.pathForType(type), constants.adapter.resourcePrefix + type);
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js
new file mode 100644
index 0000000..c7deabb
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/alert-message-widget-test.js
@@ -0,0 +1,91 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('alert-message-widget', 'AlertMessageWidgetComponent', {
+ needs: []
+});
+
+test('isExpanded is toggled on click', function() {
+ expect(2);
+
+ var message = Ember.Object.create({ isExpanded: false});
+
+ var component = this.subject({
+ message: message
+ });
+
+ Ember.run(function() {
+ component.click();
+ });
+
+ equal(component.get('message.isExpanded'), true, 'isExpanded is set to true');
+
+ Ember.run(function() {
+ component.click();
+ });
+
+ equal(component.get('message.isExpanded'), false, 'isExpanded is set to false');
+});
+
+test('removeLater should be called on click', function() {
+ expect(1);
+
+ var message = Ember.Object.create({ isExpanded: false});
+
+ var targetObject = {
+ removeLater: function() {
+ ok(true, 'External removeLater called');
+ }
+ };
+
+ var component = this.subject({
+ targetObject: targetObject,
+ removeLater: 'removeLater',
+ message: message
+ });
+
+ Ember.run(function() {
+ component.click();
+ });
+
+ Ember.run(function() {
+ component.click();
+ });
+});
+
+test('remove action should call external removeMessage', function() {
+ expect(1);
+
+ var targetObject = {
+ removeMessage: function() {
+ ok(true, 'External removeMessage called');
+ }
+ };
+
+ var component = this.subject({
+ targetObject: targetObject,
+ removeMessage: 'removeMessage'
+ });
+
+ Ember.run(function() {
+ component.send('remove', {});
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/collapsible-widget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/collapsible-widget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/collapsible-widget-test.js
new file mode 100644
index 0000000..3af6d7d
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/collapsible-widget-test.js
@@ -0,0 +1,44 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('collapsible-widget', 'CollapsibleWidgetComponent');
+
+test('Component expand/collapse toggle action', function () {
+ expect(1);
+
+ var targetObject = {
+ expanded: function() {
+ ok(true, 'External expanded called');
+ }
+ };
+
+ var component = this.subject({
+ targetObject: targetObject,
+ isExpanded: 'isExpanded',
+ expanded: 'expanded'
+ });
+
+ var $component = this.append();
+
+ Ember.run(function() {
+ component.set('isExpanded', false);
+ component.send('toggle', {});
+ });
+});
[09/12] ambari git commit: AMBARI-9676. Initial Hive View Submission
to Contrib (alexantonenko)
Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceResourceManager.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceResourceManager.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceResourceManager.java
new file mode 100644
index 0000000..31d9e23
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceResourceManager.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.resources;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.utils.FilteringStrategy;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.resources.PersonalCRUDResourceManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Object that provides CRUD operations for resource objects
+ */
+public class FileResourceResourceManager extends PersonalCRUDResourceManager<FileResourceItem> {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(FileResourceResourceManager.class);
+
+ /**
+ * Constructor
+ * @param context View Context instance
+ */
+ public FileResourceResourceManager(ViewContext context) {
+ super(FileResourceItem.class, context);
+ }
+
+ @Override
+ public FileResourceItem create(FileResourceItem object) {
+ return super.create(object);
+ }
+
+ @Override
+ public FileResourceItem read(Integer id) throws ItemNotFound {
+ return super.read(id);
+ }
+
+ @Override
+ public void delete(Integer resourceId) throws ItemNotFound {
+ super.delete(resourceId);
+ }
+
+ @Override
+ public List<FileResourceItem> readAll(FilteringStrategy filteringStrategy) {
+ return super.readAll(filteringStrategy);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceResourceProvider.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceResourceProvider.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceResourceProvider.java
new file mode 100644
index 0000000..5b9fc65
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceResourceProvider.java
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.resources;
+
+import com.google.inject.Inject;
+import org.apache.ambari.view.*;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
+import org.apache.ambari.view.hive.resources.PersonalCRUDResourceManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Resource provider for resource
+ */
+public class FileResourceResourceProvider implements ResourceProvider<FileResourceItem> {
+ @Inject
+ ViewContext context;
+
+ protected FileResourceResourceManager resourceManager = null;
+ protected final static Logger LOG =
+ LoggerFactory.getLogger(FileResourceResourceProvider.class);
+
+ protected synchronized FileResourceResourceManager getResourceManager() {
+ if (resourceManager == null) {
+ resourceManager = new FileResourceResourceManager(context);
+ }
+ return resourceManager;
+ }
+
+ @Override
+ public FileResourceItem getResource(String resourceId, Set<String> properties) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ try {
+ return getResourceManager().read(Integer.valueOf(resourceId));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NoSuchResourceException(resourceId);
+ }
+ }
+
+ @Override
+ public Set<FileResourceItem> getResources(ReadRequest readRequest) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ return new HashSet<FileResourceItem>(getResourceManager().readAll(
+ new OnlyOwnersFilteringStrategy(this.context.getUsername())));
+ }
+
+ @Override
+ public void createResource(String s, Map<String, Object> stringObjectMap) throws SystemException, ResourceAlreadyExistsException, NoSuchResourceException, UnsupportedPropertyException {
+ FileResourceItem item = null;
+ try {
+ item = new FileResourceItem(stringObjectMap);
+ } catch (InvocationTargetException e) {
+ throw new SystemException("error on creating resource", e);
+ } catch (IllegalAccessException e) {
+ throw new SystemException("error on creating resource", e);
+ }
+ getResourceManager().create(item);
+ }
+
+ @Override
+ public boolean updateResource(String resourceId, Map<String, Object> stringObjectMap) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ FileResourceItem item = null;
+ try {
+ item = new FileResourceItem(stringObjectMap);
+ } catch (InvocationTargetException e) {
+ throw new SystemException("error on updating resource", e);
+ } catch (IllegalAccessException e) {
+ throw new SystemException("error on updating resource", e);
+ }
+ try {
+ getResourceManager().update(item, Integer.valueOf(resourceId));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NoSuchResourceException(resourceId);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean deleteResource(String resourceId) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ try {
+ getResourceManager().delete(Integer.valueOf(resourceId));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NoSuchResourceException(resourceId);
+ }
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceService.java
new file mode 100644
index 0000000..2993280
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/resources/FileResourceService.java
@@ -0,0 +1,181 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.resources;
+
+import com.google.inject.Inject;
+import org.apache.ambari.view.ViewResourceHandler;
+import org.apache.ambari.view.hive.BaseService;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
+import org.apache.ambari.view.hive.utils.NotFoundFormattedException;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.*;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.List;
+
+/**
+ * Servlet for Resources
+ * API:
+ * GET /:id
+ * read resource
+ * POST /
+ * create new resource
+ * GET /
+ * get all resource of current user
+ */
+public class FileResourceService extends BaseService {
+ @Inject
+ ViewResourceHandler handler;
+
+ protected FileResourceResourceManager resourceManager = null;
+ protected final static Logger LOG =
+ LoggerFactory.getLogger(FileResourceService.class);
+
+ protected synchronized FileResourceResourceManager getResourceManager() {
+ if (resourceManager == null) {
+ resourceManager = new FileResourceResourceManager(context);
+ }
+ return resourceManager;
+ }
+
+ /**
+ * Get single item
+ */
+ @GET
+ @Path("{id}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getOne(@PathParam("id") String id) {
+ try {
+ FileResourceItem FileResourceItem = null;
+ FileResourceItem = getResourceManager().read(Integer.valueOf(id));
+ JSONObject object = new JSONObject();
+ object.put("fileResource", FileResourceItem);
+ return Response.ok(object).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Delete single item
+ */
+ @DELETE
+ @Path("{id}")
+ public Response delete(@PathParam("id") String id) {
+ try {
+ getResourceManager().delete(Integer.valueOf(id));
+ return Response.status(204).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Get all resources
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getList() {
+ try {
+ LOG.debug("Getting all resources");
+ List items = getResourceManager().readAll(
+ new OnlyOwnersFilteringStrategy(this.context.getUsername())); //TODO: move strategy to PersonalCRUDRM
+
+ JSONObject object = new JSONObject();
+ object.put("fileResources", items);
+ return Response.ok(object).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Update item
+ */
+ @PUT
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response update(ResourceRequest request,
+ @PathParam("id") String id) {
+ try {
+ getResourceManager().update(request.fileResource, Integer.valueOf(id));
+ return Response.status(204).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Create resource
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response create(ResourceRequest request, @Context HttpServletResponse response,
+ @Context UriInfo ui) {
+ try {
+ getResourceManager().create(request.fileResource);
+
+ FileResourceItem item = null;
+
+ item = getResourceManager().read(request.fileResource.getId());
+
+ response.setHeader("Location",
+ String.format("%s/%s", ui.getAbsolutePath().toString(), request.fileResource.getId()));
+
+ JSONObject object = new JSONObject();
+ object.put("fileResource", item);
+ return Response.ok(object).status(201).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Wrapper object for json mapping
+ */
+ public static class ResourceRequest {
+ public FileResourceItem fileResource;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQuery.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQuery.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQuery.java
new file mode 100644
index 0000000..fd75714
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQuery.java
@@ -0,0 +1,96 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.savedQueries;
+
+import org.apache.ambari.view.hive.persistence.utils.PersonalResource;
+import org.apache.commons.beanutils.BeanUtils;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+
+/**
+ * Bean to represent saved query
+ */
+public class SavedQuery implements Serializable, PersonalResource {
+ private String queryFile;
+ private String dataBase;
+ private String title;
+ private String shortQuery;
+
+ private Integer id;
+ private String owner;
+
+ public SavedQuery() {}
+ public SavedQuery(Map<String, Object> stringObjectMap) throws InvocationTargetException, IllegalAccessException {
+ BeanUtils.populate(this, stringObjectMap);
+ }
+
+ @Override
+ public Integer getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getOwner() {
+ return owner;
+ }
+
+ @Override
+ public void setOwner(String owner) {
+ this.owner = owner;
+ }
+
+ public String getQueryFile() {
+ return queryFile;
+ }
+
+ public void setQueryFile(String queryFile) {
+ this.queryFile = queryFile;
+ }
+
+ public String getDataBase() {
+ return dataBase;
+ }
+
+ public void setDataBase(String dataBase) {
+ this.dataBase = dataBase;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getShortQuery() {
+ return shortQuery;
+ }
+
+ public void setShortQuery(String shortQuery) {
+ this.shortQuery = shortQuery;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryResourceManager.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryResourceManager.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryResourceManager.java
new file mode 100644
index 0000000..a3c0f1b
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryResourceManager.java
@@ -0,0 +1,132 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.savedQueries;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.utils.FilteringStrategy;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.resources.PersonalCRUDResourceManager;
+import org.apache.ambari.view.hive.utils.*;
+import org.apache.ambari.view.hive.utils.HdfsUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Object that provides CRUD operations for query objects
+ */
+public class SavedQueryResourceManager extends PersonalCRUDResourceManager<SavedQuery> {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(SavedQueryResourceManager.class);
+
+ /**
+ * Constructor
+ * @param context View Context instance
+ */
+ private SavedQueryResourceManager(ViewContext context) {
+ super(SavedQuery.class, context);
+ }
+
+ //TODO: move all context-singletones to ContextController or smth like that
+ private static Map<String, SavedQueryResourceManager> viewSingletonObjects = new HashMap<String, SavedQueryResourceManager>();
+ public static SavedQueryResourceManager getInstance(ViewContext context) {
+ if (!viewSingletonObjects.containsKey(context.getInstanceName()))
+ viewSingletonObjects.put(context.getInstanceName(), new SavedQueryResourceManager(context));
+ return viewSingletonObjects.get(context.getInstanceName());
+ }
+ static Map<String, SavedQueryResourceManager> getViewSingletonObjects() {
+ return viewSingletonObjects;
+ }
+
+ @Override
+ public SavedQuery create(SavedQuery object) {
+ object = super.create(object);
+ try {
+
+ if (object.getQueryFile() == null || object.getQueryFile().isEmpty()) {
+ createDefaultQueryFile(object);
+ }
+
+ } catch (ServiceFormattedException e) {
+ cleanupAfterErrorAndThrowAgain(object, e);
+ }
+ return object;
+ }
+
+ private void createDefaultQueryFile(SavedQuery object) {
+ String userScriptsPath = context.getProperties().get("scripts.dir");
+ if (userScriptsPath == null) {
+ String msg = "scripts.dir is not configured!";
+ LOG.error(msg);
+ throw new MisconfigurationFormattedException("scripts.dir");
+ }
+
+ String normalizedName = String.format("hive-query-%d", object.getId());
+ String timestamp = new SimpleDateFormat("yyyy-MM-dd_hh-mm").format(new Date());
+ String baseFileName = String.format(userScriptsPath +
+ "/%s-%s", normalizedName, timestamp);
+
+ String newFilePath = HdfsUtil.findUnallocatedFileName(context, baseFileName, ".hql");
+ HdfsUtil.putStringToFile(context, newFilePath, "");
+
+ object.setQueryFile(newFilePath);
+ getStorage().store(SavedQuery.class, object);
+ }
+
+ @Override
+ public SavedQuery read(Integer id) throws ItemNotFound {
+ SavedQuery savedQuery = super.read(id);
+ fillShortQueryField(savedQuery);
+ return savedQuery;
+ }
+
+ private void fillShortQueryField(SavedQuery savedQuery) {
+ if (savedQuery.getQueryFile() != null) {
+ FilePaginator paginator = new FilePaginator(savedQuery.getQueryFile(), context);
+ String query = null;
+ try {
+ query = paginator.readPage(0);
+ } catch (IOException e) {
+ LOG.error("Can't read query file " + savedQuery.getQueryFile());
+ return;
+ } catch (InterruptedException e) {
+ LOG.error("Can't read query file " + savedQuery.getQueryFile());
+ return;
+ }
+ savedQuery.setShortQuery(query.substring(0, (query.length() > 42)?42:query.length()));
+ }
+ getStorage().store(SavedQuery.class, savedQuery);
+ }
+
+ @Override
+ public List<SavedQuery> readAll(FilteringStrategy filteringStrategy) {
+ return super.readAll(filteringStrategy);
+ }
+
+ @Override
+ public void delete(Integer resourceId) throws ItemNotFound {
+ super.delete(resourceId);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryResourceProvider.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryResourceProvider.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryResourceProvider.java
new file mode 100644
index 0000000..20df9e6
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryResourceProvider.java
@@ -0,0 +1,104 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.savedQueries;
+
+import com.google.inject.Inject;
+import org.apache.ambari.view.*;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
+import org.apache.ambari.view.hive.resources.PersonalCRUDResourceManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Resource provider for SavedQuery
+ */
+public class SavedQueryResourceProvider implements ResourceProvider<SavedQuery> {
+ @Inject
+ ViewContext context;
+
+ protected SavedQueryResourceManager resourceManager = null;
+ protected final static Logger LOG =
+ LoggerFactory.getLogger(SavedQueryResourceProvider.class);
+
+ protected synchronized SavedQueryResourceManager getResourceManager() {
+ return SavedQueryResourceManager.getInstance(context);
+ }
+
+ @Override
+ public SavedQuery getResource(String resourceId, Set<String> properties) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ try {
+ return getResourceManager().read(Integer.valueOf(resourceId));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NoSuchResourceException(resourceId);
+ }
+ }
+
+ @Override
+ public Set<SavedQuery> getResources(ReadRequest readRequest) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ return new HashSet<SavedQuery>(getResourceManager().readAll(
+ new OnlyOwnersFilteringStrategy(this.context.getUsername())));
+ }
+
+ @Override
+ public void createResource(String s, Map<String, Object> stringObjectMap) throws SystemException, ResourceAlreadyExistsException, NoSuchResourceException, UnsupportedPropertyException {
+ SavedQuery item = null;
+ try {
+ item = new SavedQuery(stringObjectMap);
+ } catch (InvocationTargetException e) {
+ throw new SystemException("error on creating resource", e);
+ } catch (IllegalAccessException e) {
+ throw new SystemException("error on creating resource", e);
+ }
+ getResourceManager().create(item);
+ }
+
+ @Override
+ public boolean updateResource(String resourceId, Map<String, Object> stringObjectMap) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ SavedQuery item = null;
+ try {
+ item = new SavedQuery(stringObjectMap);
+ } catch (InvocationTargetException e) {
+ throw new SystemException("error on updating resource", e);
+ } catch (IllegalAccessException e) {
+ throw new SystemException("error on updating resource", e);
+ }
+ try {
+ getResourceManager().update(item, Integer.valueOf(resourceId));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NoSuchResourceException(resourceId);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean deleteResource(String resourceId) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ try {
+ getResourceManager().delete(Integer.valueOf(resourceId));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NoSuchResourceException(resourceId);
+ }
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryService.java
new file mode 100644
index 0000000..f6d5f41
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryService.java
@@ -0,0 +1,184 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.savedQueries;
+
+import com.google.inject.Inject;
+import org.apache.ambari.view.ViewResourceHandler;
+import org.apache.ambari.view.hive.BaseService;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
+import org.apache.ambari.view.hive.resources.PersonalCRUDResourceManager;
+import org.apache.ambari.view.hive.utils.NotFoundFormattedException;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.*;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.List;
+
+/**
+ * Servlet for queries
+ * API:
+ * GET /:id
+ * read SavedQuery
+ * POST /
+ * create new SavedQuery
+ * Required: title, queryFile
+ * GET /
+ * get all SavedQueries of current user
+ */
+public class SavedQueryService extends BaseService {
+ @Inject
+ ViewResourceHandler handler;
+
+ protected SavedQueryResourceManager resourceManager = null;
+ protected final static Logger LOG =
+ LoggerFactory.getLogger(SavedQueryService.class);
+
+ protected synchronized SavedQueryResourceManager getResourceManager() {
+ return SavedQueryResourceManager.getInstance(context);
+ }
+
+ protected void setResourceManager(SavedQueryResourceManager resourceManager) {
+ this.resourceManager = resourceManager;
+ }
+
+ /**
+ * Get single item
+ */
+ @GET
+ @Path("{queryId}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getOne(@PathParam("queryId") String queryId) {
+ try {
+ SavedQuery savedQuery = null;
+ savedQuery = getResourceManager().read(Integer.valueOf(queryId));
+ JSONObject object = new JSONObject();
+ object.put("savedQuery", savedQuery);
+ return Response.ok(object).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Delete single item
+ */
+ @DELETE
+ @Path("{queryId}")
+ public Response delete(@PathParam("queryId") String queryId) {
+ try {
+ getResourceManager().delete(Integer.valueOf(queryId));
+ return Response.status(204).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Get all SavedQueries
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getList() {
+ try {
+ LOG.debug("Getting all SavedQuery");
+ List allSavedQueries = getResourceManager().readAll(
+ new OnlyOwnersFilteringStrategy(this.context.getUsername())); //TODO: move strategy to PersonalCRUDRM
+
+ JSONObject object = new JSONObject();
+ object.put("savedQueries", allSavedQueries);
+ return Response.ok(object).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Update item
+ */
+ @PUT
+ @Path("{queryId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response update(SavedQueryRequest request,
+ @PathParam("queryId") String queryId) {
+ try {
+ getResourceManager().update(request.savedQuery, Integer.valueOf(queryId));
+ return Response.status(204).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Create savedQuery
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response create(SavedQueryRequest request, @Context HttpServletResponse response,
+ @Context UriInfo ui) {
+ try {
+ getResourceManager().create(request.savedQuery);
+
+ SavedQuery item = null;
+
+ item = getResourceManager().read(request.savedQuery.getId());
+
+ response.setHeader("Location",
+ String.format("%s/%s", ui.getAbsolutePath().toString(), request.savedQuery.getId()));
+
+ JSONObject object = new JSONObject();
+ object.put("savedQuery", item);
+ return Response.ok(object).status(201).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Wrapper object for json mapping
+ */
+ public static class SavedQueryRequest {
+ public SavedQuery savedQuery;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java
new file mode 100644
index 0000000..4a58e38
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDF.java
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.udfs;
+
+import org.apache.ambari.view.hive.persistence.utils.PersonalResource;
+import org.apache.commons.beanutils.BeanUtils;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+
+/**
+ * Bean to represent UDF
+ */
+public class UDF implements Serializable, PersonalResource {
+ private String name;
+ private String classname;
+ private Integer fileResource;
+
+ private Integer id;
+ private String owner;
+
+ public UDF() {}
+ public UDF(Map<String, Object> stringObjectMap) throws InvocationTargetException, IllegalAccessException {
+ BeanUtils.populate(this, stringObjectMap);
+ }
+
+ @Override
+ public Integer getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getOwner() {
+ return owner;
+ }
+
+ @Override
+ public void setOwner(String owner) {
+ this.owner = owner;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getClassname() {
+ return classname;
+ }
+
+ public void setClassname(String classname) {
+ this.classname = classname;
+ }
+
+ public Integer getFileResource() {
+ return fileResource;
+ }
+
+ public void setFileResource(Integer fileResource) {
+ this.fileResource = fileResource;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDFResourceManager.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDFResourceManager.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDFResourceManager.java
new file mode 100644
index 0000000..cb4264f
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDFResourceManager.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.udfs;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.utils.FilteringStrategy;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.resources.PersonalCRUDResourceManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Object that provides CRUD operations for udf objects
+ */
+public class UDFResourceManager extends PersonalCRUDResourceManager<UDF> {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(UDFResourceManager.class);
+
+ /**
+ * Constructor
+ * @param context View Context instance
+ */
+ public UDFResourceManager(ViewContext context) {
+ super(UDF.class, context);
+ }
+
+ @Override
+ public UDF read(Integer id) throws ItemNotFound {
+ return super.read(id);
+ }
+
+ @Override
+ public List<UDF> readAll(FilteringStrategy filteringStrategy) {
+ return super.readAll(filteringStrategy);
+ }
+
+ @Override
+ public UDF create(UDF object) {
+ return super.create(object);
+ }
+
+ @Override
+ public void delete(Integer resourceId) throws ItemNotFound {
+ super.delete(resourceId);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDFResourceProvider.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDFResourceProvider.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDFResourceProvider.java
new file mode 100644
index 0000000..70994eb
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDFResourceProvider.java
@@ -0,0 +1,107 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.udfs;
+
+import com.google.inject.Inject;
+import org.apache.ambari.view.*;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
+import org.apache.ambari.view.hive.resources.PersonalCRUDResourceManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Resource provider for udf
+ */
+public class UDFResourceProvider implements ResourceProvider<UDF> {
+ @Inject
+ ViewContext context;
+
+ protected UDFResourceManager resourceManager = null;
+ protected final static Logger LOG =
+ LoggerFactory.getLogger(UDFResourceProvider.class);
+
+ protected synchronized UDFResourceManager getResourceManager() {
+ if (resourceManager == null) {
+ resourceManager = new UDFResourceManager(context);
+ }
+ return resourceManager;
+ }
+
+ @Override
+ public UDF getResource(String resourceId, Set<String> properties) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ try {
+ return getResourceManager().read(Integer.valueOf(resourceId));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NoSuchResourceException(resourceId);
+ }
+ }
+
+ @Override
+ public Set<UDF> getResources(ReadRequest readRequest) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ return new HashSet<UDF>(getResourceManager().readAll(
+ new OnlyOwnersFilteringStrategy(this.context.getUsername())));
+ }
+
+ @Override
+ public void createResource(String s, Map<String, Object> stringObjectMap) throws SystemException, ResourceAlreadyExistsException, NoSuchResourceException, UnsupportedPropertyException {
+ UDF item = null;
+ try {
+ item = new UDF(stringObjectMap);
+ } catch (InvocationTargetException e) {
+ throw new SystemException("error on creating resource", e);
+ } catch (IllegalAccessException e) {
+ throw new SystemException("error on creating resource", e);
+ }
+ getResourceManager().create(item);
+ }
+
+ @Override
+ public boolean updateResource(String resourceId, Map<String, Object> stringObjectMap) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ UDF item = null;
+ try {
+ item = new UDF(stringObjectMap);
+ } catch (InvocationTargetException e) {
+ throw new SystemException("error on updating resource", e);
+ } catch (IllegalAccessException e) {
+ throw new SystemException("error on updating resource", e);
+ }
+ try {
+ getResourceManager().update(item, Integer.valueOf(resourceId));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NoSuchResourceException(resourceId);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean deleteResource(String resourceId) throws SystemException, NoSuchResourceException, UnsupportedPropertyException {
+ try {
+ getResourceManager().delete(Integer.valueOf(resourceId));
+ } catch (ItemNotFound itemNotFound) {
+ throw new NoSuchResourceException(resourceId);
+ }
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDFService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDFService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDFService.java
new file mode 100644
index 0000000..864d5b0
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/resources/udfs/UDFService.java
@@ -0,0 +1,196 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.udfs;
+
+import com.google.inject.Inject;
+import org.apache.ambari.view.ViewResourceHandler;
+import org.apache.ambari.view.hive.BaseService;
+import org.apache.ambari.view.hive.persistence.utils.ItemNotFound;
+import org.apache.ambari.view.hive.persistence.utils.OnlyOwnersFilteringStrategy;
+import org.apache.ambari.view.hive.resources.PersonalCRUDResourceManager;
+import org.apache.ambari.view.hive.resources.resources.FileResourceItem;
+import org.apache.ambari.view.hive.resources.resources.FileResourceResourceManager;
+import org.apache.ambari.view.hive.utils.NotFoundFormattedException;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.*;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.List;
+
+/**
+ * Servlet for UDFs
+ * API:
+ * GET /:id
+ * read udf
+ * POST /
+ * create new udf
+ * GET /
+ * get all udf of current user
+ */
+public class UDFService extends BaseService {
+ @Inject
+ ViewResourceHandler handler;
+
+ protected UDFResourceManager resourceManager = null;
+ protected FileResourceResourceManager fileResourceResourceManager = null;
+ protected final static Logger LOG =
+ LoggerFactory.getLogger(UDFService.class);
+
+ protected synchronized UDFResourceManager getResourceManager() {
+ if (resourceManager == null) {
+ resourceManager = new UDFResourceManager(context);
+ }
+ return resourceManager;
+ }
+
+ protected synchronized FileResourceResourceManager getFileResourceResourceManager() {
+ if (fileResourceResourceManager == null) {
+ fileResourceResourceManager = new FileResourceResourceManager(context);
+ }
+ return fileResourceResourceManager;
+ }
+
+ /**
+ * Get single item
+ */
+ @GET
+ @Path("{id}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getOne(@PathParam("id") String id) {
+ try {
+ UDF UDF = null;
+ UDF = getResourceManager().read(Integer.valueOf(id));
+ JSONObject object = new JSONObject();
+ object.put("udf", UDF);
+ return Response.ok(object).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Delete single item
+ */
+ @DELETE
+ @Path("{id}")
+ public Response delete(@PathParam("id") String id) {
+ try {
+ getResourceManager().delete(Integer.valueOf(id));
+ return Response.status(204).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Get all UDFs
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response getList() {
+ try {
+ LOG.debug("Getting all udf");
+ List items = getResourceManager().readAll(
+ new OnlyOwnersFilteringStrategy(this.context.getUsername())); //TODO: move strategy to PersonalCRUDRM
+
+ JSONObject object = new JSONObject();
+ object.put("udfs", items);
+ return Response.ok(object).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Update item
+ */
+ @PUT
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response update(UDFRequest request,
+ @PathParam("id") String id) {
+ try {
+ if (request.udf.getFileResource() != null)
+ getFileResourceResourceManager().read(request.udf.getFileResource());
+ getResourceManager().update(request.udf, Integer.valueOf(id));
+ return Response.status(204).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Create udf
+ */
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response create(UDFRequest request, @Context HttpServletResponse response,
+ @Context UriInfo ui) {
+ try {
+ if (request.udf.getFileResource() != null)
+ getFileResourceResourceManager().read(request.udf.getFileResource());
+ getResourceManager().create(request.udf);
+
+ UDF item = null;
+
+ item = getResourceManager().read(request.udf.getId());
+
+ response.setHeader("Location",
+ String.format("%s/%s", ui.getAbsolutePath().toString(), request.udf.getId()));
+
+ JSONObject object = new JSONObject();
+ object.put("udf", item);
+ return Response.ok(object).status(201).build();
+ } catch (WebApplicationException ex) {
+ throw ex;
+ } catch (ItemNotFound itemNotFound) {
+ throw new NotFoundFormattedException(itemNotFound.getMessage(), itemNotFound);
+ } catch (Exception ex) {
+ throw new ServiceFormattedException(ex.getMessage(), ex);
+ }
+ }
+
+ /**
+ * Wrapper object for json mapping
+ */
+ public static class UDFRequest {
+ public UDF udf;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/BadRequestFormattedException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/BadRequestFormattedException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/BadRequestFormattedException.java
new file mode 100644
index 0000000..0641af1
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/BadRequestFormattedException.java
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.utils;
+
+public class BadRequestFormattedException extends ServiceFormattedException {
+ private final static int STATUS = 400;
+
+ public BadRequestFormattedException(String message, Throwable exception) {
+ super(message, exception, STATUS);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/FilePaginator.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/FilePaginator.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/FilePaginator.java
new file mode 100644
index 0000000..f27e1f9
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/FilePaginator.java
@@ -0,0 +1,127 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.utils;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.hadoop.fs.FSDataInputStream;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import static java.lang.Math.ceil;
+
+/**
+ * Pagination for HDFS file implementation
+ */
+public class FilePaginator {
+ public static int MB = 1024*1024;
+ public static int PAGE_SIZE = 1*MB;
+
+ private String filePath;
+ private HdfsApi hdfsApi;
+
+ /**
+ * Constructor
+ * @param filePath Path to file on HDFS
+ * @param context View Context instance
+ */
+ public FilePaginator(String filePath, ViewContext context) {
+ this.filePath = filePath;
+ hdfsApi = HdfsApi.getInstance(context);
+ }
+
+ /**
+ * Set page size
+ * @param PAGE_SIZE size
+ */
+ public static void setPageSize(int PAGE_SIZE) {
+ FilePaginator.PAGE_SIZE = PAGE_SIZE;
+ }
+
+ /**
+ * Get page count
+ * @return page count
+ * @throws java.io.IOException
+ * @throws InterruptedException
+ */
+ public long pageCount() throws IOException, InterruptedException {
+ return (long)
+ ceil( hdfsApi.getFileStatus(filePath).getLen() / ((double)PAGE_SIZE) );
+ }
+
+ /**
+ * Read one page of size PAGE_SIZE
+ * @param page page index
+ * @return data in UTF-8
+ * @throws java.io.IOException
+ * @throws InterruptedException
+ */
+ public String readPage(long page) throws IOException, InterruptedException {
+ FSDataInputStream stream = hdfsApi.open(filePath);
+ try {
+ stream.seek(page * PAGE_SIZE);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Page " + page + " does not exists");
+ }
+
+ byte[] buffer = new byte[PAGE_SIZE];
+ int readCount = 0;
+ int read = 0;
+ while(read < PAGE_SIZE) {
+ try {
+ readCount = stream.read(buffer, read, PAGE_SIZE-read);
+ } catch (IOException e) {
+ stream.close();
+ throw e;
+ }
+ if (readCount == -1)
+ break;
+ read += readCount;
+ }
+ if (read != 0) {
+ byte[] readData = Arrays.copyOfRange(buffer, 0, read);
+ return new String(readData, Charset.forName("UTF-8"));
+ } else {
+ if (page == 0) {
+ return "";
+ }
+ throw new IllegalArgumentException("Page " + page + " does not exists");
+ }
+ }
+
+ public String readFull(long sizeLimit) throws IOException, InterruptedException {
+ StringBuilder builder = new StringBuilder();
+ int i = 0;
+ while (true) {
+ try {
+ builder.append(readPage(i++));
+ } catch (IllegalArgumentException ex) {
+ break;
+ }
+ if (sizeLimit != -1 && (i+1)*PAGE_SIZE > sizeLimit)
+ break;
+ }
+ return builder.toString();
+ }
+
+ public String readFull() throws IOException, InterruptedException {
+ return readFull(-1);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java
new file mode 100644
index 0000000..9a120fa
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsApi.java
@@ -0,0 +1,399 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.utils;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.*;
+import org.apache.hadoop.fs.permission.FsPermission;
+import org.apache.hadoop.hdfs.DistributedFileSystem;
+import org.apache.hadoop.hdfs.web.WebHdfsFileSystem;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.json.simple.JSONArray;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URI;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * HDFS Business Delegate
+ */
+public class HdfsApi {
+ private Configuration conf = new Configuration();
+
+ private FileSystem fs;
+ private Map<String, String> params;
+
+ private UserGroupInformation ugi;
+ private final static Logger LOG =
+ LoggerFactory.getLogger(HdfsApi.class);
+
+ /**
+ * Constructor
+ * @param defaultFs hdfs uri
+ * @param username user.name
+ * @throws java.io.IOException
+ * @throws InterruptedException
+ */
+ public HdfsApi(final String defaultFs, String username, Map<String, String> params) throws IOException,
+ InterruptedException {
+ this.params = params;
+ Thread.currentThread().setContextClassLoader(null);
+ conf.set("fs.hdfs.impl", DistributedFileSystem.class.getName());
+ conf.set("fs.webhdfs.impl", WebHdfsFileSystem.class.getName());
+ conf.set("fs.file.impl", "org.apache.hadoop.fs.LocalFileSystem");
+ ugi = UserGroupInformation.createProxyUser(username, getProxyUser());
+ fs = ugi.doAs(new PrivilegedExceptionAction<FileSystem>() {
+ public FileSystem run() throws IOException {
+ return FileSystem.get(URI.create(defaultFs), conf);
+ }
+ });
+ }
+
+ private UserGroupInformation getProxyUser() throws IOException {
+ UserGroupInformation proxyuser;
+ if (params.containsKey("proxyuser")) {
+ proxyuser = UserGroupInformation.createRemoteUser(params.get("proxyuser"));
+ } else {
+ proxyuser = UserGroupInformation.getCurrentUser();
+ }
+
+ proxyuser.setAuthenticationMethod(getAuthenticationMethod());
+ return proxyuser;
+ }
+
+ private UserGroupInformation.AuthenticationMethod getAuthenticationMethod() {
+ UserGroupInformation.AuthenticationMethod authMethod;
+ if (params.containsKey("auth")) {
+ authMethod = UserGroupInformation.AuthenticationMethod.valueOf(params.get("auth"));
+ } else {
+ authMethod = UserGroupInformation.AuthenticationMethod.SIMPLE;
+ }
+ return authMethod;
+ }
+
+ /**
+ * List dir operation
+ * @param path path
+ * @return array of FileStatus objects
+ * @throws java.io.FileNotFoundException
+ * @throws java.io.IOException
+ * @throws InterruptedException
+ */
+ public FileStatus[] listdir(final String path) throws FileNotFoundException,
+ IOException, InterruptedException {
+ return ugi.doAs(new PrivilegedExceptionAction<FileStatus[]>() {
+ public FileStatus[] run() throws FileNotFoundException, Exception {
+ return fs.listStatus(new Path(path));
+ }
+ });
+ }
+
+ /**
+ * Get file status
+ * @param path path
+ * @return file status
+ * @throws java.io.IOException
+ * @throws java.io.FileNotFoundException
+ * @throws InterruptedException
+ */
+ public FileStatus getFileStatus(final String path) throws IOException,
+ FileNotFoundException, InterruptedException {
+ return ugi.doAs(new PrivilegedExceptionAction<FileStatus>() {
+ public FileStatus run() throws FileNotFoundException, IOException {
+ return fs.getFileStatus(new Path(path));
+ }
+ });
+ }
+
+ /**
+ * Make directory
+ * @param path path
+ * @return success
+ * @throws java.io.IOException
+ * @throws InterruptedException
+ */
+ public boolean mkdir(final String path) throws IOException,
+ InterruptedException {
+ return ugi.doAs(new PrivilegedExceptionAction<Boolean>() {
+ public Boolean run() throws Exception {
+ return fs.mkdirs(new Path(path));
+ }
+ });
+ }
+
+ /**
+ * Rename
+ * @param src source path
+ * @param dst destination path
+ * @return success
+ * @throws java.io.IOException
+ * @throws InterruptedException
+ */
+ public boolean rename(final String src, final String dst) throws IOException,
+ InterruptedException {
+ return ugi.doAs(new PrivilegedExceptionAction<Boolean>() {
+ public Boolean run() throws Exception {
+ return fs.rename(new Path(src), new Path(dst));
+ }
+ });
+ }
+
+ /**
+ * Delete
+ * @param path path
+ * @param recursive delete recursive
+ * @return success
+ * @throws java.io.IOException
+ * @throws InterruptedException
+ */
+ public boolean delete(final String path, final boolean recursive)
+ throws IOException, InterruptedException {
+ return ugi.doAs(new PrivilegedExceptionAction<Boolean>() {
+ public Boolean run() throws Exception {
+ return fs.delete(new Path(path), recursive);
+ }
+ });
+ }
+
+ /**
+ * Home directory
+ * @return home directory
+ * @throws Exception
+ */
+ public Path getHomeDir() throws Exception {
+ return ugi.doAs(new PrivilegedExceptionAction<Path>() {
+ public Path run() throws IOException {
+ return fs.getHomeDirectory();
+ }
+ });
+ }
+
+ /**
+ * Hdfs Status
+ * @return home directory
+ * @throws Exception
+ */
+ public FsStatus getStatus() throws Exception {
+ return ugi.doAs(new PrivilegedExceptionAction<FsStatus>() {
+ public FsStatus run() throws IOException {
+ return fs.getStatus();
+ }
+ });
+ }
+
+ /**
+ * Create file
+ * @param path path
+ * @param overwrite overwrite existent file
+ * @return output stream
+ * @throws java.io.IOException
+ * @throws InterruptedException
+ */
+ public FSDataOutputStream create(final String path, final boolean overwrite)
+ throws IOException, InterruptedException {
+ return ugi.doAs(new PrivilegedExceptionAction<FSDataOutputStream>() {
+ public FSDataOutputStream run() throws Exception {
+ return fs.create(new Path(path), overwrite);
+ }
+ });
+ }
+
+ /**
+ * Open file
+ * @param path path
+ * @return input stream
+ * @throws java.io.IOException
+ * @throws InterruptedException
+ */
+ public FSDataInputStream open(final String path) throws IOException,
+ InterruptedException {
+ return ugi.doAs(new PrivilegedExceptionAction<FSDataInputStream>() {
+ public FSDataInputStream run() throws Exception {
+ return fs.open(new Path(path));
+ }
+ });
+ }
+
+ /**
+ * Copy file
+ * @param src source path
+ * @param dest destination path
+ * @return success
+ * @throws java.io.IOException
+ * @throws InterruptedException
+ */
+ public void copy(final String src, final String dest) throws IOException,
+ InterruptedException {
+ boolean result = ugi.doAs(new PrivilegedExceptionAction<Boolean>() {
+ public Boolean run() throws Exception {
+ return FileUtil.copy(fs, new Path(src), fs, new Path(dest), false, conf);
+ }
+ });
+ if (!result) {
+ throw new ServiceFormattedException("Can't copy source file from " + src + " to " + dest);
+ }
+ }
+
+ public boolean exists(final String newFilePath) throws IOException, InterruptedException {
+ return ugi.doAs(new PrivilegedExceptionAction<Boolean>() {
+ public Boolean run() throws Exception {
+ return fs.exists(new Path(newFilePath));
+ }
+ });
+ }
+
+ /**
+ * Converts a Hadoop permission into a Unix permission symbolic representation
+ * (i.e. -rwxr--r--) or default if the permission is NULL.
+ *
+ * @param p
+ * Hadoop permission.
+ * @return the Unix permission symbolic representation or default if the
+ * permission is NULL.
+ */
+ private static String permissionToString(FsPermission p) {
+ return (p == null) ? "default" : "-" + p.getUserAction().SYMBOL
+ + p.getGroupAction().SYMBOL + p.getOtherAction().SYMBOL;
+ }
+
+ /**
+ * Converts a Hadoop <code>FileStatus</code> object into a JSON array object.
+ * It replaces the <code>SCHEME://HOST:PORT</code> of the path with the
+ * specified URL.
+ * <p/>
+ *
+ * @param status
+ * Hadoop file status.
+ * @return The JSON representation of the file status.
+ */
+
+ public static Map<String, Object> fileStatusToJSON(FileStatus status) {
+ Map<String, Object> json = new LinkedHashMap<String, Object>();
+ json.put("path", status.getPath().toString());
+ json.put("isDirectory", status.isDirectory());
+ json.put("len", status.getLen());
+ json.put("owner", status.getOwner());
+ json.put("group", status.getGroup());
+ json.put("permission", permissionToString(status.getPermission()));
+ json.put("accessTime", status.getAccessTime());
+ json.put("modificationTime", status.getModificationTime());
+ json.put("blockSize", status.getBlockSize());
+ json.put("replication", status.getReplication());
+ return json;
+ }
+
+ /**
+ * Converts a Hadoop <code>FileStatus</code> array into a JSON array object.
+ * It replaces the <code>SCHEME://HOST:PORT</code> of the path with the
+ * specified URL.
+ * <p/>
+ *
+ * @param status
+ * Hadoop file status array.
+ * @return The JSON representation of the file status array.
+ */
+ @SuppressWarnings("unchecked")
+ public static JSONArray fileStatusToJSON(FileStatus[] status) {
+ JSONArray json = new JSONArray();
+ if (status != null) {
+ for (FileStatus s : status) {
+ json.add(fileStatusToJSON(s));
+ }
+ }
+ return json;
+ }
+
+
+ private static Map<String, HdfsApi> viewSingletonObjects = new HashMap<String, HdfsApi>();
+ /**
+ * Returns HdfsApi object specific to instance
+ * @param context View Context instance
+ * @return Hdfs business delegate object
+ */
+ public static HdfsApi getInstance(ViewContext context) {
+ if (!viewSingletonObjects.containsKey(context.getInstanceName()))
+ viewSingletonObjects.put(context.getInstanceName(), connectToHDFSApi(context));
+ return viewSingletonObjects.get(context.getInstanceName());
+ }
+
+ public static void setInstance(ViewContext context, HdfsApi api) {
+ viewSingletonObjects.put(context.getInstanceName(), api);
+ }
+
+ public static HdfsApi connectToHDFSApi(ViewContext context) {
+ HdfsApi api = null;
+ Thread.currentThread().setContextClassLoader(null);
+
+ String defaultFS = context.getProperties().get("webhdfs.url");
+ if (defaultFS == null) {
+ String message = "webhdfs.url is not configured!";
+ LOG.error(message);
+ throw new MisconfigurationFormattedException("webhdfs.url");
+ }
+
+ try {
+ api = new HdfsApi(defaultFS, getHdfsUsername(context), getHdfsAuthParams(context));
+ LOG.info("HdfsApi connected OK");
+ } catch (IOException e) {
+ String message = "HdfsApi IO error: " + e.getMessage();
+ LOG.error(message);
+ throw new ServiceFormattedException(message, e);
+ } catch (InterruptedException e) {
+ String message = "HdfsApi Interrupted error: " + e.getMessage();
+ LOG.error(message);
+ throw new ServiceFormattedException(message, e);
+ }
+ return api;
+ }
+
+ private static Map<String, String> getHdfsAuthParams(ViewContext context) {
+ String auth = context.getProperties().get("webhdfs.auth");
+ Map<String, String> params = new HashMap<String, String>();
+ if (auth == null || auth.isEmpty()) {
+ auth = "auth=SIMPLE";
+ }
+ for(String param : auth.split(";")) {
+ String[] keyvalue = param.split("=");
+ if (keyvalue.length != 2) {
+ LOG.error("Can not parse authentication param " + param + " in " + auth);
+ continue;
+ }
+ params.put(keyvalue[0], keyvalue[1]);
+ }
+ return params;
+ }
+
+ public static String getHdfsUsername(ViewContext context) {
+ String userName = context.getProperties().get("webhdfs.username");
+ if (userName == null || userName.compareTo("null") == 0 || userName.compareTo("") == 0)
+ userName = context.getUsername();
+ return userName;
+ }
+
+ public static void dropAllConnections() {
+ viewSingletonObjects.clear();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java
new file mode 100644
index 0000000..c1c5495
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/HdfsUtil.java
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.utils;
+
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.hadoop.fs.FSDataOutputStream;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+public class HdfsUtil {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(HdfsUtil.class);
+
+ /**
+ * Write string to file with overwriting
+ * @param filePath path to file
+ * @param content new content of file
+ */
+ public static void putStringToFile(ViewContext context, String filePath, String content) {
+ HdfsApi hdfs = HdfsApi.getInstance(context);
+
+ FSDataOutputStream stream;
+ try {
+ stream = hdfs.create(filePath, true);
+ stream.writeBytes(content);
+ stream.close();
+ } catch (IOException e) {
+ throw new ServiceFormattedException("Could not write file " + filePath, e);
+ } catch (InterruptedException e) {
+ throw new ServiceFormattedException("Could not write file " + filePath, e);
+ }
+ }
+
+
+ /**
+ * Increment index appended to filename until find first unallocated file
+ * @param fullPathAndFilename path to file and prefix for filename
+ * @param extension file extension
+ * @return if fullPathAndFilename="/tmp/file",extension=".txt" then filename will be like "/tmp/file_42.txt"
+ */
+ public static String findUnallocatedFileName(ViewContext context, String fullPathAndFilename, String extension) {
+ HdfsApi hdfs = HdfsApi.getInstance(context);
+
+ int triesCount = 0;
+ String newFilePath;
+ boolean isUnallocatedFilenameFound;
+
+ try {
+ do {
+ newFilePath = String.format(fullPathAndFilename + "%s" + extension, (triesCount == 0) ? "" : "_" + triesCount);
+ LOG.debug("Trying to find free filename " + newFilePath);
+
+ isUnallocatedFilenameFound = !hdfs.exists(newFilePath);
+ if (isUnallocatedFilenameFound) {
+ LOG.debug("File created successfully!");
+ }
+
+ triesCount += 1;
+ } while (!isUnallocatedFilenameFound);
+ } catch (IOException e) {
+ throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
+ } catch (InterruptedException e) {
+ throw new ServiceFormattedException("Error in creation: " + e.toString(), e);
+ }
+
+ return newFilePath;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/MisconfigurationFormattedException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/MisconfigurationFormattedException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/MisconfigurationFormattedException.java
new file mode 100644
index 0000000..314b00b
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/MisconfigurationFormattedException.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.utils;
+
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.HashMap;
+
+public class MisconfigurationFormattedException extends WebApplicationException {
+ private final static int STATUS = 500;
+ private final static String message = "Parameter \"%s\" is set to null";
+ private final static Logger LOG =
+ LoggerFactory.getLogger(MisconfigurationFormattedException.class);
+
+ public MisconfigurationFormattedException(String name) {
+ super(errorEntity(name));
+ }
+
+ protected static Response errorEntity(String name) {
+ HashMap<String, Object> response = new HashMap<String, Object>();
+ response.put("message", String.format(message, name));
+ response.put("trace", null);
+ response.put("status", STATUS);
+ return Response.status(STATUS).entity(new JSONObject(response)).type(MediaType.APPLICATION_JSON).build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/NotFoundFormattedException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/NotFoundFormattedException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/NotFoundFormattedException.java
new file mode 100644
index 0000000..91b11cf
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/NotFoundFormattedException.java
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.utils;
+
+public class NotFoundFormattedException extends ServiceFormattedException {
+ private final static int STATUS = 404;
+
+ public NotFoundFormattedException(String message, Throwable exception) {
+ super(message, exception, STATUS);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/ServiceFormattedException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/ServiceFormattedException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/ServiceFormattedException.java
new file mode 100644
index 0000000..19b324c
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/utils/ServiceFormattedException.java
@@ -0,0 +1,78 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.utils;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.json.simple.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.security.AccessControlException;
+import java.util.HashMap;
+
+public class ServiceFormattedException extends WebApplicationException {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(ServiceFormattedException.class);
+
+ public ServiceFormattedException(String message) {
+ super(errorEntity(message, null, suggestStatus(null), null));
+ }
+
+ public ServiceFormattedException(String message, Throwable exception) {
+ super(errorEntity(message, exception, suggestStatus(exception), null));
+ }
+
+ public ServiceFormattedException(String message, Throwable exception, int status) {
+ super(errorEntity(message, exception, status, null));
+ }
+
+ public ServiceFormattedException(String message, Exception ex, String curl) {
+ super(errorEntity(message, ex, suggestStatus(ex), curl));
+ }
+
+ private static int suggestStatus(Throwable exception) {
+ int status = 500;
+ if (exception == null) return status;
+ if (exception instanceof AccessControlException) {
+ status = 403;
+ }
+ return status;
+ }
+
+ protected static Response errorEntity(String message, Throwable e, int status, String header) {
+ HashMap<String, Object> response = new HashMap<String, Object>();
+ response.put("message", message);
+ String trace = null;
+ if (e != null)
+ trace = ExceptionUtils.getStackTrace(e);
+ response.put("trace", trace);
+ response.put("status", status);
+
+ if(message != null) LOG.error(message);
+ if(trace != null) LOG.debug(trace);
+
+ Response.ResponseBuilder responseBuilder = Response.status(status).entity(new JSONObject(response)).type(MediaType.APPLICATION_JSON);
+ if (header != null)
+ responseBuilder.header("X-INFO", header);
+ return responseBuilder.build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/.gitignore
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/.gitignore b/contrib/views/hive/src/main/resources/ui/hive-web/.gitignore
new file mode 100644
index 0000000..043148a
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/.gitignore
@@ -0,0 +1,35 @@
+# Numerous always-ignore extensions
+*.diff
+*.err
+*.orig
+*.log
+*.rej
+*.swo
+*.swp
+*.vi
+*~
+*.sass-cache
+
+# OS or Editor folders
+.DS_Store
+.cache
+.project
+.settings
+.tmproj
+dist
+nbproject
+Thumbs.db
+
+# NPM packages folder.
+node_modules/
+
+bower_components/
+
+node/
+
+# Brunch folder for temporary files.
+tmp/
+
+public/
+
+_generators/
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/.jshintrc
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/.jshintrc b/contrib/views/hive/src/main/resources/ui/hive-web/.jshintrc
new file mode 100644
index 0000000..c1fe863
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/.jshintrc
@@ -0,0 +1,32 @@
+{
+ "predef": [
+ "document",
+ "window",
+ "-Promise"
+ ],
+ "browser" : true,
+ "boss" : true,
+ "curly": true,
+ "debug": false,
+ "devel": true,
+ "eqeqeq": false,
+ "evil": true,
+ "forin": false,
+ "immed": false,
+ "laxbreak": false,
+ "newcap": true,
+ "noarg": true,
+ "noempty": false,
+ "nonew": false,
+ "nomen": false,
+ "onevar": false,
+ "plusplus": false,
+ "regexp": false,
+ "undef": true,
+ "sub": true,
+ "strict": false,
+ "white": false,
+ "eqnull": true,
+ "esnext": true,
+ "unused": true
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js b/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js
new file mode 100644
index 0000000..db65f1f
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/Brocfile.js
@@ -0,0 +1,46 @@
+/**
+ * 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.
+ */
+
+/* global require, module */
+
+var EmberApp = require('ember-cli/lib/broccoli/ember-app');
+
+var app = new EmberApp({
+ 'ember-cli-selectize': {
+ //valid values are `default`, `bootstrap2`, `bootstrap3` or false
+ 'theme': 'bootstrap3'
+ },
+ hinting: false
+});
+
+app.import('bower_components/bootstrap/dist/js/bootstrap.js');
+app.import('bower_components/bootstrap/dist/css/bootstrap.css');
+app.import('bower_components/bootstrap/dist/css/bootstrap.css.map', {
+ destDir: 'assets'
+});
+
+app.import('bower_components/ember-i18n/lib/i18n.js');
+app.import('bower_components/ember-i18n/lib/i18n-plurals.js');
+
+app.import('vendor/codemirror/codemirror-min.js');
+app.import('vendor/codemirror/sql-hint.js');
+app.import('vendor/codemirror/show-hint.js');
+app.import('vendor/codemirror/codemirror.css');
+app.import('vendor/codemirror/show-hint.css');
+
+module.exports = app.toTree();
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/README.md
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/README.md b/contrib/views/hive/src/main/resources/ui/hive-web/README.md
new file mode 100644
index 0000000..2237863
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/README.md
@@ -0,0 +1,14 @@
+<!---
+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](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.
+-->
[08/12] ambari git commit: AMBARI-9676. Initial Hive View Submission
to Contrib (alexantonenko)
Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/adapters/application.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/adapters/application.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/adapters/application.js
new file mode 100644
index 0000000..e838a3d
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/adapters/application.js
@@ -0,0 +1,44 @@
+/**
+ * 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 DS from 'ember-data';
+import constants from 'hive/utils/constants';
+
+export default DS.RESTAdapter.extend({
+ headers: {
+ "X-Requested-By": "ambari"
+ },
+
+ buildURL: function () {
+ var version = constants.adapter.version,
+ instanceName = constants.adapter.instance;
+
+ var params = window.location.pathname.split('/').filter(function (param) {
+ return !!param;
+ });
+
+ if (params[params.length - 3] === 'HIVE') {
+ version = params[params.length - 2];
+ instanceName = params[params.length - 1];
+ }
+
+ var prefix = constants.adapter.apiPrefix + version + constants.adapter.instancePrefix + instanceName;
+ var url = this._super.apply(this, arguments);
+ return prefix + url;
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/adapters/database.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/adapters/database.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/adapters/database.js
new file mode 100644
index 0000000..cdbd43b
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/adapters/database.js
@@ -0,0 +1,25 @@
+/**
+ * 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 application from './application';
+
+export default application.extend({
+ pathForType: function (type) {
+ return 'resources/ddl/' + type;
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/adapters/file.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/adapters/file.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/adapters/file.js
new file mode 100644
index 0000000..47894c1
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/adapters/file.js
@@ -0,0 +1,26 @@
+/**
+ * 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 application from './application';
+import constants from 'hive/utils/constants';
+
+export default application.extend({
+ pathForType: function (type) {
+ return constants.adapter.resourcePrefix + type;
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/app.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/app.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/app.js
new file mode 100644
index 0000000..f35a5a3
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/app.js
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import Resolver from 'ember/resolver';
+import loadInitializers from 'ember/load-initializers';
+import config from './config/environment';
+
+Ember.MODEL_FACTORY_INJECTIONS = true;
+
+var App = Ember.Application.extend({
+ modulePrefix: config.modulePrefix,
+ podModulePrefix: config.podModulePrefix,
+ Resolver: Resolver
+});
+
+loadInitializers(App, config.modulePrefix);
+
+export default App;
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/.gitkeep b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/.gitkeep
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js
new file mode 100644
index 0000000..fd8c1d7
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/alert-message-widget.js
@@ -0,0 +1,35 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Component.extend({
+ click: function () {
+ this.toggleProperty('message.isExpanded');
+
+ if (!this.get('message.isExpanded')) {
+ this.sendAction('removeLater', this.get('message'));
+ }
+ },
+
+ actions: {
+ remove: function () {
+ this.sendAction('removeMessage', this.get('message'));
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js
new file mode 100644
index 0000000..4425b59
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/collapsible-widget.js
@@ -0,0 +1,33 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Component.extend({
+ tagName: 'collapsible',
+
+ actions: {
+ toggle: function () {
+ this.toggleProperty('isExpanded');
+
+ if (this.get('isExpanded')) {
+ this.sendAction('expanded', this.get('heading'), this.get('toggledParam'));
+ }
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/column-filter-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/column-filter-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/column-filter-widget.js
new file mode 100644
index 0000000..2622d4b
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/column-filter-widget.js
@@ -0,0 +1,56 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Component.extend({
+ tagName: 'column-filter',
+
+ didInsertElement: function () {
+ if (this.get('filterValue')) {
+ this.send('sendFilter');
+ }
+ },
+
+ isSorted: (function() {
+ var sortProperties = this.get('sortProperties');
+
+ if (sortProperties) {
+ return sortProperties[0] === this.get('column.property');
+ } else {
+ return false;
+ }
+ }).property('sortProperties'),
+
+ actions: {
+ sendSort: function () {
+ this.sendAction('columnSorted', this.get('column.property'));
+ },
+
+ sendFilter: function (params) {
+ if (params) {
+ this.set('filterValue', Ember.Object.create({
+ min: params.from,
+ max: params.to
+ }));
+ }
+
+ this.sendAction('columnFiltered', this.get('column.property'), this.get('filterValue'));
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/date-range-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/date-range-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/date-range-widget.js
new file mode 100644
index 0000000..790cb35
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/date-range-widget.js
@@ -0,0 +1,62 @@
+/**
+ * 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.
+ */
+
+ /* globals moment */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+ displayFromDate: function () {
+ return moment(this.get('dateRange.from')).format('MM/DD/YYYY');
+ }.property('dateRange.from'),
+
+ displayToDate: function () {
+ return moment(this.get('dateRange.to')).format('MM/DD/YYYY');
+ }.property('dateRange.to'),
+
+ didInsertElement: function () {
+ var self = this;
+
+ if (!this.get('dateRange.min') && !this.get('dateRange.max')) {
+ this.set('dateRange.max', new Date());
+ }
+
+ if (!this.get('dateRange.from') && !this.get('dateRange.to')) {
+ this.set('dateRange.from', this.get('dateRange.min'));
+ this.set('dateRange.to', this.get('dateRange.max'));
+ }
+
+ this.$(".fromDate").datepicker({
+ defaultDate: this.get("dateRange.from"),
+ onSelect: function (selectedDate) {
+ self.$(".toDate").datepicker("option", "minDate", selectedDate);
+ self.set('dateRange.from', new Date(selectedDate));
+ self.sendAction('rangeChanged', self.get('dateRange'));
+ }
+ });
+
+ this.$(".toDate").datepicker({
+ defaultDate: this.get('dateRange.to'),
+ onSelect: function (selectedDate) {
+ self.$(".fromDate").datepicker("option", "maxDate", selectedDate);
+ self.set('dateRange.to', new Date(selectedDate + " 23:59"));
+ self.sendAction('rangeChanged', self.get('dateRange'));
+ }
+ });
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/expander-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/expander-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/expander-widget.js
new file mode 100644
index 0000000..d46a490
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/expander-widget.js
@@ -0,0 +1,30 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Component.extend({
+ tagName: 'expander',
+
+ actions: {
+ toggle: function () {
+ this.toggleProperty('isExpanded');
+ this.$('.accordion-body').toggle(200);
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/extended-input.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/extended-input.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/extended-input.js
new file mode 100644
index 0000000..ee2161c
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/extended-input.js
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.TextField.extend(Ember.I18n.TranslateableProperties, {
+ didInsertElement: function () {
+ var dynamicValue = this.get('dynamicValue');
+ var dynamicContext = this.get('dynamicContext');
+
+ if (dynamicValue && dynamicContext) {
+ this.set('value', dynamicContext.get(dynamicValue));
+ }
+ },
+
+ sendValueChanged: function () {
+ var dynamicValue = this.get('dynamicValue');
+ var dynamicContext = this.get('dynamicContext');
+
+ if (dynamicValue && dynamicContext) {
+ dynamicContext.set(dynamicValue, this.get('value'));
+ }
+
+ this.sendAction('valueChanged');
+ },
+
+ keyUp: function (e) {
+ //if user has pressed enter
+ if (e.keyCode === 13) {
+ this.sendAction('valueSearched', this.get('value'));
+ } else {
+ Ember.run.debounce(this, this.get('sendValueChanged'), 300);
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js
new file mode 100644
index 0000000..b9179bb
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/modal-widget.js
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend(Ember.I18n.TranslateableProperties, {
+ show: function() {
+ this.$('.modal').modal().on('hidden.bs.modal', function () {
+ this.sendAction('close');
+ }.bind(this));
+ }.on('didInsertElement'),
+
+ actions: {
+ ok: function () {
+ this.$('.modal').modal('hide');
+ this.sendAction('ok');
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/no-bubbling.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/no-bubbling.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/no-bubbling.js
new file mode 100644
index 0000000..4b723b1
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/no-bubbling.js
@@ -0,0 +1,31 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Component.extend({
+ didInsertElement: function () {
+ var self = this;
+
+ this.$().click(function (e) {
+ e.stopPropagation();
+
+ self.sendAction('click', self.get('data'));
+ });
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js
new file mode 100644
index 0000000..ad98beb
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/number-range-widget.js
@@ -0,0 +1,52 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Component.extend({
+ didInsertElement: function () {
+ var self = this;
+
+ var slider;
+
+ this.$(function() {
+ if (!self.get('numberRange.from') && !self.get('numberRange.to')) {
+ self.get('numberRange').set('from', self.get('numberRange.min'));
+ self.get('numberRange').set('to', self.get('numberRange.max'));
+ }
+
+ slider = self.$( ".slider" ).slider({
+ range: true,
+ min: self.get('numberRange.min'),
+ max: self.get('numberRange.max'),
+ units: self.get('numberRange.units'),
+ values: [ self.get('numberRange.from'), self.get('numberRange.to') ],
+ slide: function (event, ui) {
+ self.set('numberRange.from', ui.values[0]);
+ self.set('numberRange.to', ui.values[1]);
+ },
+
+ change: function () {
+ self.sendAction('rangeChanged', self.get('numberRange'));
+ }
+ });
+ });
+
+ this.set('slider', slider);
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/panel-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/panel-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/panel-widget.js
new file mode 100644
index 0000000..1f41a43
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/panel-widget.js
@@ -0,0 +1,30 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Component.extend(Ember.I18n.TranslateableProperties, {
+ tagName: 'panel',
+
+ actions: {
+ sendMenuItemAction: function(action) {
+ this.set('menuItemAction', action);
+ this.sendAction('menuItemAction');
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/popover-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/popover-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/popover-widget.js
new file mode 100644
index 0000000..e1bd4c5
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/popover-widget.js
@@ -0,0 +1,33 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Component.extend(Ember.I18n.TranslateableProperties, {
+ tagName: 'popover',
+ attributeBindings: ['title', 'content:data-content'],
+ didInsertElement: function() {
+ this.$().popover({
+ html: true,
+ placement: 'left',
+ trigger: 'hover'
+ });
+
+ this.$().attr('data-content', this.$('.hide').html());
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/progress-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/progress-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/progress-widget.js
new file mode 100644
index 0000000..2a86ee7
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/progress-widget.js
@@ -0,0 +1,45 @@
+/**
+ * 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.
+ */
+
+// this component is a work in progress
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+ formattedStages: Ember.ArrayProxy.create({ content: [] }),
+
+ formatStages: function() {
+ var formattedStages = this.get('formattedStages');
+ var stages = this.get('stages');
+ var currentStage;
+
+ stages.forEach(function(stage) {
+ currentStage = formattedStages.findBy('name', stage.get('name'));
+
+ if(currentStage) {
+ currentStage.set('progress', 'width: %@%'.fmt(stage.get('value')))
+ } else {
+ formattedStages.pushObject(Ember.Object.create({
+ name: stage.get('name'),
+ className: stage.get('className'),
+ progress: 'width: %@%'.fmt(stage.get('value'))
+ }));
+ }
+ });
+ }.observes('stages.@each.value').on('init')
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/query-editor.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/query-editor.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/query-editor.js
new file mode 100644
index 0000000..01ba37e
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/query-editor.js
@@ -0,0 +1,121 @@
+/**
+ * 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.
+ */
+
+/* global CodeMirror */
+
+/**
+/* Copyright (C) 2014 by Marijn Haverbeke <ma...@gmail.com> and others
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+
+ * The above copyright notice and this permission notice shal l be included in
+ * all copies or substantial portions of the Software.
+*/
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+ tagName: 'query-editor',
+
+ tablesChanged: function () {
+ //Format hintOptions object as needed by the CodeMirror
+ //http://stackoverflow.com/questions/20023381/codemirror-how-add-tables-to-sql-hint
+ this.set('editor.options.hintOptions', { tables: this.get('tables') });
+ }.observes('tables'),
+
+ getColumnsHint: function (cm, tableName) {
+ var callback = function () {
+ CodeMirror.showHint(cm);
+ };
+
+ this.sendAction('columnsNeeded', tableName, callback);
+ },
+
+ initEditor: function () {
+ var editor,
+ updateSize,
+ self = this;
+
+ updateSize = function () {
+ editor.setSize(self.$(this).width(), self.$(this).height());
+ editor.refresh();
+ };
+
+ this.set('editor', CodeMirror.fromTextArea(document.getElementById('code-mirror'), {
+ mode: 'text/x-hive',
+ hint: CodeMirror.hint.sql,
+ indentWithTabs: true,
+ smartIndent: true,
+ lineNumbers: true,
+ matchBrackets : true,
+ autofocus: true,
+ extraKeys: {'Ctrl-Space': 'autocomplete'}
+ }));
+
+ CodeMirror.commands.autocomplete = function (cm) {
+ var lastWord = cm.getValue().split(' ').pop();
+
+ //if user wants to fill in a column
+ if (lastWord.indexOf('.') > -1) {
+ lastWord = lastWord.split('.')[0];
+
+ self.getColumnsHint(cm, lastWord);
+ } else {
+ CodeMirror.showHint(cm);
+ }
+ };
+
+ editor = this.get('editor');
+
+ editor.on('cursorActivity', function() {
+ self.set('highlightedText', editor.getSelections());
+ });
+
+ editor.setValue(this.get('query') || '');
+
+ editor.on('change', function (instance) {
+ Ember.run(function() {
+ self.set('query', instance.getValue());
+ });
+ });
+
+ this.$('.CodeMirror').resizable({
+ handles: 's',
+
+ resize: function () {
+ Ember.run.debounce(this, updateSize, 150);
+ }
+ }).find('.ui-resizable-s').addClass('grip fa fa-reorder');
+
+ this.tablesChanged();
+ }.on('didInsertElement'),
+
+ updateValue: function() {
+ var query = this.get('query');
+ var editor = this.get('editor');
+
+ if (editor.getValue() !== query) {
+ editor.setValue(query || '');
+ }
+ }.observes('query')
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/select-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/select-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/select-widget.js
new file mode 100644
index 0000000..8bd7a22
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/select-widget.js
@@ -0,0 +1,66 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Component.extend(Ember.I18n.TranslateableProperties, {
+ tagName: 'dropdown',
+
+ selectedLabel: function () {
+ var value;
+
+ //if there's an item selected, retrieve the property to be displayed as a label
+ if (this.get('selectedValue') && this.get('labelPath')) {
+ value = this.get('selectedValue').get(this.get('labelPath'));
+
+ if (value) {
+ return value;
+ }
+ }
+
+ //else if a default label has been provided, use it as the selected label.
+ if (this.get('defaultLabel')) {
+ return this.get('defaultLabel');
+ }
+ }.property('selectedValue'),
+
+ didInsertElement: function () {
+ //if no selected item nor defaultLabel, set the selected value
+ if (!this.get('selectedValue') && !this.get('defaultLabel') && this.get('items')) {
+ this.set('selectedValue', this.get('items').objectAt(0));
+ }
+ },
+
+ actions: {
+ select: function (item){
+ this.set('selectedValue', item);
+ },
+
+ add: function () {
+ this.sendAction('itemAdded');
+ },
+
+ edit: function (item) {
+ this.sendAction('itemEdited', item);
+ },
+
+ remove: function (item) {
+ this.sendAction('itemRemoved', item);
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/tabs-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/tabs-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/tabs-widget.js
new file mode 100644
index 0000000..2a898f5
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/tabs-widget.js
@@ -0,0 +1,64 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Component.extend({
+ tagName: 'tabs',
+
+ didInsertElement: function () {
+ var tabToActivate,
+ tabs = this.get('tabs');
+
+ if (tabs.get('length')) {
+ tabToActivate = tabs.find(function (tab) {
+ return tab.get('active');
+ });
+
+ if (tabToActivate) {
+ this.set('selectedTab', tabToActivate);
+ } else {
+ this.set('selectedTab', tabs.objectAt(0));
+ }
+ }
+ },
+
+ activateTab: function () {
+ var selectedTab = this.get('selectedTab');
+
+ selectedTab.set('active', true);
+
+ this.get('tabs').without(selectedTab).forEach(function (tab) {
+ tab.set('active', false);
+ });
+ }.observes('selectedTab'),
+
+ removeEnabled: function () {
+ return this.get('canRemove') && this.get('tabs.length') > 1;
+ }.property('tabs.@each'),
+
+ actions: {
+ remove: function (tab) {
+ this.sendAction('removeClicked', tab);
+ },
+
+ selectTab: function (tab) {
+ this.set('selectedTab', tab);
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/tree-view.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/tree-view.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/tree-view.js
new file mode 100644
index 0000000..cd63f52
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/tree-view.js
@@ -0,0 +1,23 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Component.extend({
+ tagName: 'tree-view'
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js
new file mode 100644
index 0000000..8226c9c
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/components/typeahead-widget.js
@@ -0,0 +1,44 @@
+/**
+ * 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 Typeahead from 'ember-cli-selectize/components/ember-selectize';
+import Ember from 'ember';
+
+export default Typeahead.extend({
+ didInsertElement: function() {
+ this._super();
+
+ if(!this.get('selection') && this.get('content.firstObject')) {
+ this.set('selection', this.get('content.firstObject'));
+ }
+
+ this.selectize.on('dropdown_close', Ember.$.proxy(this.onClose, this));
+ },
+
+ onClose: function() {
+ if(!this.get('selection') && this.get('prevSelection')) {
+ this.set('selection', this.get('prevSelection'));
+ }
+ },
+
+ _onItemAdd: function(value) {
+ this._super(value);
+
+ this.set('prevSelection', this.get('selection'));
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/.gitkeep b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/.gitkeep
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/alerts.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/alerts.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/alerts.js
new file mode 100644
index 0000000..be70566
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/alerts.js
@@ -0,0 +1,47 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.ArrayController.extend({
+ pushObject: function (object) {
+ object.typeClass = 'alert-' + object.type;
+ this._super(object);
+ this.removeLater(object);
+ },
+
+ removeLater: function (object) {
+ var self = this;
+
+ Ember.run.later(function() {
+ if (!object.isExpanded) {
+ self.removeObject(object);
+ }
+ }, 5000);
+ },
+
+ actions: {
+ remove: function (message) {
+ this.removeObject(message);
+ },
+
+ removeLater: function (message) {
+ this.removeLater(message);
+ }
+ }
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/columns.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/columns.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/columns.js
new file mode 100644
index 0000000..38d1b95
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/columns.js
@@ -0,0 +1,104 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.ObjectController.extend({
+ pageCount: 10,
+
+ init: function () {
+ this._super();
+
+ var databaseAdapter = this.container.lookup('adapter:database');
+ var baseUrl = databaseAdapter.buildURL() + '/' +
+ databaseAdapter.pathForType(constants.namingConventions.database) + '/';
+
+ this.set('baseUrl', baseUrl);
+ },
+
+ getColumnsPage: function (databaseName, table, searchTerm, firstSearchPage) {
+ var defer = Ember.RSVP.defer();
+
+ var url = this.get('baseUrl') +
+ databaseName +
+ '/table/' +
+ table.get('name');
+
+ url += '.page?searchId&count=' + this.get('pageCount');
+ url += '&columns=3,5';
+
+ if (searchTerm) {
+ url += '&searchId=searchColumns' + '&like=' + searchTerm;
+
+ if (firstSearchPage) {
+ url += '&first=true';
+ }
+ } else if (!table.get('columns.length')) {
+ url += '&first=true';
+ }
+
+ Ember.$.getJSON(url).then(function (data) {
+ Ember.run(function () {
+ var columns;
+
+ columns = data.rows.map(function (row) {
+ return Ember.Object.create({
+ name: row[0],
+ type: row[1]
+ });
+ });
+
+ defer.resolve({
+ columns: columns,
+ hasNext: data.hasNext
+ });
+ });
+ }, function (err) {
+ defer.reject(err);
+ });
+
+ return defer.promise;
+ },
+
+ getColumns: function (databaseName, tableName) {
+ var defer = Ember.RSVP.defer();
+
+ var url = this.get('baseUrl') +
+ databaseName +
+ '/table/' +
+ tableName;
+
+ Ember.$.getJSON(url).then(function (data) {
+ Ember.run(function () {
+ var columns = data.columns.map(function (column) {
+ return Ember.Object.create({
+ name: column[0],
+ type: column[1]
+ });
+ });
+
+ defer.resolve(columns);
+ });
+ }, function (err) {
+ defer.reject(err);
+ });
+
+ return defer.promise;
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js
new file mode 100644
index 0000000..6a259ba
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/databases.js
@@ -0,0 +1,354 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.ArrayController.extend({
+ pageCount: 10,
+
+ needs: [ constants.namingConventions.tables,
+ constants.namingConventions.columns,
+ constants.namingConventions.openQueries ],
+
+ openQueries: Ember.computed.alias('controllers.' + constants.namingConventions.openQueries),
+ dbTables: Ember.computed.alias('controllers.' + constants.namingConventions.tables),
+ dbColumns: Ember.computed.alias('controllers.' + constants.namingConventions.columns),
+
+ _handleTablesError: function (err) {
+ this.send('addAlert', constants.alerts.error, err.responseText, "alerts.errors.get.tables");
+ this.set('isLoading', false);
+ },
+
+ _handleColumnsError: function (err) {
+ this.send('addAlert', constants.alerts.error, err.responseText, "alerts.errors.get.columns");
+ this.set('isLoading', false);
+ },
+
+ init: function () {
+ this._super();
+
+ var databaseAdapter = this.container.lookup('adapter:database');
+ var baseUrl = databaseAdapter.buildURL() + '/' +
+ databaseAdapter.pathForType(constants.namingConventions.database) + '/';
+
+ this.set('baseUrl', baseUrl);
+ this.set('tableSearchResults', Ember.Object.create());
+
+ this.set('tabs', Ember.ArrayProxy.create({ content: Ember.A([
+ Ember.Object.create({
+ name: Ember.I18n.t('titles.explorer'),
+ visible: true,
+ view: constants.namingConventions.databaseTree
+ }),
+ Ember.Object.create({
+ name: Ember.I18n.t('titles.results'),
+ view: constants.namingConventions.databaseSearch
+ })
+ ])}));
+ },
+
+ setTablePageAvailability: function (database) {
+ var result;
+
+ if (database.get('hasNext')) {
+ result = true;
+ } else if (database.tables.length > database.get('visibleTables.length')) {
+ //if there are hidden tables
+ result = true;
+ }
+
+ database.set('canGetNextPage', result);
+ },
+
+ setColumnPageAvailability: function (table) {
+ var result;
+
+ if (table.get('hasNext')) {
+ result = true;
+ } else if (table.columns.length > table.get('visibleColumns.length')) {
+ //if there are hidden columns
+ result = true;
+ }
+
+ table.set('canGetNextPage', result);
+ },
+
+ selectedDatabaseChanged: function () {
+ var self = this,
+ database = this.get('selectedDatabase');
+
+ //if no selected database or database has already fully loaded tables
+ if (!database || (database.tables && !database.get('hasNext'))) {
+ return;
+ }
+
+ this.set('isLoading', true);
+
+ this.get('dbTables').getTables(database.get('name')).then(function (tables) {
+ var mappedTables = {};
+
+ //don't use Ember.Object.set since it can be very expensive for large collections (e.g. 15000 tables),
+ //thus we should not do any bindings directly on the 'tables' collection.
+ database.tables = tables;
+
+ tables.forEach(function (table) {
+ mappedTables[table.name] = [];
+ });
+
+ self.set('openQueries.selectedTables', mappedTables);
+ self.set('isLoading', false);
+ }, function (err) {
+ self._handleTablesError(err);
+ });
+ }.observes('selectedDatabase'),
+
+ getNextColumnPage: function (database, table) {
+ var self = this;
+
+ this.set('isLoading', true);
+
+ if (!table.columns) {
+ table.columns = [];
+ table.set('visibleColumns', []);
+ }
+
+ this.get('dbColumns').getColumnsPage(database.get('name'), table).then(function (result) {
+ table.columns.pushObjects(result.columns);
+ table.get('visibleColumns').pushObjects(result.columns);
+ table.set('hasNext', result.hasNext);
+
+ self.setColumnPageAvailability(table);
+ self.set('isLoading', false);
+ }, function (err) {
+ self._handleColumnsError(err);
+ });
+ },
+
+ getNextTablePage: function (database) {
+ var self = this;
+
+ this.set('isLoading', true);
+
+ if (!database.tables) {
+ database.tables = [];
+ database.set('visibleTables', []);
+ }
+
+ this.get('dbTables').getTablesPage(database).then(function (result) {
+ database.tables.pushObjects(result.tables);
+ database.get('visibleTables').pushObjects(result.tables);
+ database.set('hasNext', result.hasNext);
+
+ self.setTablePageAvailability(database);
+ self.set('isLoading', false);
+ }, function (err) {
+ self._handleTablesError(err);
+ });
+ },
+
+ getAllColumns: function (tableName) {
+ var defer = Ember.RSVP.defer();
+ var self = this;
+ var database = this.get('selectedDatabase');
+ var table = database.get('tables').findBy('name', tableName);
+
+ //if all the columns were already loaded for this table, do not get them again.
+ if (table.columns && !table.get('hasNext')) {
+ defer.resolve();
+ } else {
+ this.set('isLoading', true);
+
+ this.get('dbColumns').getColumns(database.get('name'), tableName).then(function (columns) {
+ table.columns = columns;
+ table.set('hasNext', false);
+
+ self.get('openQueries.selectedTables')[tableName] = columns.mapProperty('name');
+ self.set('isLoading', false);
+
+ defer.resolve();
+ }, function (err) {
+ self._handleColumnsError(err);
+ defer.reject(err);
+ });
+ }
+
+ return defer.promise;
+ },
+
+ actions: {
+ getTables: function (dbName) {
+ var database = this.findBy('name', dbName),
+ tables = database.tables,
+ pageCount = this.get('pageCount');
+
+ if (!tables) {
+ this.getNextTablePage(database);
+ } else {
+ database.set('visibleTables', tables.slice(0, pageCount));
+ this.setTablePageAvailability(database);
+ }
+ },
+
+ getColumns: function (tableName, database) {
+ var table = database.get('visibleTables').findBy('name', tableName),
+ pageCount = this.get('pageCount'),
+ columns = table.columns;
+
+ if (!columns) {
+ this.getNextColumnPage(database, table);
+ } else {
+ table.set('visibleColumns', columns.slice(0, pageCount));
+ this.setColumnPageAvailability(table);
+ }
+ },
+
+ showMoreTables: function (database) {
+ var tables = database.tables,
+ visibleTables = database.get('visibleTables'),
+ visibleCount = visibleTables.length;
+
+ if (!tables) {
+ this.getNextTablePage(database);
+ } else {
+ if (tables.length > visibleCount) {
+ visibleTables.pushObjects(tables.slice(visibleCount, visibleCount + this.get('pageCount')));
+ this.setTablePageAvailability(database);
+ } else {
+ this.getNextTablePage(database);
+ }
+ }
+ },
+
+ showMoreColumns: function (table, database) {
+ var columns = table.columns,
+ visibleColumns = table.get('visibleColumns'),
+ visibleCount = visibleColumns.length;
+
+ if (!columns) {
+ this.getNextColumnPage(database, table);
+ } else {
+ if (columns.length > visibleCount) {
+ visibleColumns.pushObjects(columns.slice(visibleCount, visibleCount + this.get('pageCount')));
+ this.setColumnPageAvailability(table);
+ } else {
+ this.getNextColumnPage(database, table);
+ }
+ }
+ },
+
+ searchTables: function (searchTerm) {
+ var self = this,
+ resultsTab = this.get('tabs').findBy('view', constants.namingConventions.databaseSearch),
+ tableSearchResults = this.get('tableSearchResults');
+
+ resultsTab.set('visible', true);
+ this.set('selectedTab', resultsTab);
+ this.set('columnSearchTerm', '');
+ this.set('isLoading', true);
+
+ this.get('dbTables').getTablesPage(this.get('selectedDatabase'), searchTerm, true).then(function (result) {
+ tableSearchResults.set('tables', result.tables);
+ tableSearchResults.set('hasNext', result.hasNext);
+
+ self.set('isLoading', false);
+ }, function (err) {
+ self._handleTablesError(err);
+ });
+ },
+
+ searchColumns: function (searchTerm) {
+ var self = this,
+ database = this.get('selectedDatabase'),
+ resultsTab = this.get('tabs').findBy('view', constants.namingConventions.databaseSearch),
+ tables = this.get('tableSearchResults.tables');
+
+ this.set('selectedTab', resultsTab);
+
+ this.set('isLoading', true);
+
+ tables.forEach(function (table) {
+ self.get('dbColumns').getColumnsPage(database.get('name'), table, searchTerm, true).then(function (result) {
+ table.set('columns', result.columns);
+ table.set('hasNext', result.hasNext);
+
+ if (tables.indexOf(table) === tables.get('length') -1) {
+ self.set('isLoading', false);
+ }
+ }, function (err) {
+ self._handleColumnsError(err);
+ });
+ });
+ },
+
+ showMoreResultTables: function () {
+ var self = this,
+ database = this.get('selectedDatabase'),
+ tableSearchResults = this.get('tableSearchResults'),
+ searchTerm = this.get('tableSearchTerm');
+
+ this.set('isLoading', true);
+
+ this.get('dbTables').getTablesPage(database, searchTerm).then(function (tablesResult) {
+ var tables = tableSearchResults.get('tables');
+ var shouldGetColumns = tables.any(function (table) {
+ return table.get('columns.length') > 0;
+ });
+
+ tables.pushObjects(tablesResult.tables);
+ tableSearchResults.set('hasNext', tablesResult.hasNext);
+
+ //if user has already searched for columns for the previously loaded tables,
+ //load the columns search results for the newly loaded tables.
+ if (shouldGetColumns) {
+ tablesResult.tables.forEach(function (table) {
+ self.get('dbColumns').getColumnsPage(database.get('name'), table, self.get('columnSearchTerm'), true).then(function (result) {
+ table.set('columns', result.columns);
+ table.set('hasNext', result.hasNext);
+
+ if (tablesResult.tables.indexOf(table) === tablesResult.tables.get('length') -1) {
+ self.set('isLoading', false);
+ }
+ }, function (err) {
+ self._handleColumnsError(err);
+ });
+ });
+ } else {
+ self.set('isLoading', false);
+ }
+ }, function (err) {
+ self._handleTablesError(err);
+ });
+ },
+
+ showMoreResultColumns: function (table) {
+ var self = this;
+
+ this.set('isLoading', true);
+
+ this.get('dbColumns').getColumnsPage(this.get('selectedDatabase.name'), table, this.get('columnSearchTerm')).then(function (result) {
+ table.get('columns').pushObjects(result.columns);
+ table.set('hasNext', result.hasNext);
+
+ self.set('isLoading', false);
+ }, function (err) {
+ self._handleColumnsError(err);
+ });
+ }
+ }
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/file-resources.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/file-resources.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/file-resources.js
new file mode 100644
index 0000000..578fa67
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/file-resources.js
@@ -0,0 +1,22 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.ArrayController.extend({
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/history.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/history.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/history.js
new file mode 100644
index 0000000..157b917
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/history.js
@@ -0,0 +1,148 @@
+/**
+ * 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 Ember from 'ember';
+import FilterableMixin from 'hive/mixins/filterable';
+import constants from 'hive/utils/constants';
+
+export default Ember.ArrayController.extend(FilterableMixin, {
+ itemController: constants.namingConventions.job,
+
+ sortAscending: true,
+ sortProperties: ['dateSubmitted'],
+
+ init: function () {
+ var oneMonthAgo = new Date();
+ oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
+
+ this._super();
+
+ this.set('columns', Ember.ArrayProxy.create({ content: Ember.A([
+ Ember.Object.create({
+ caption: 'columns.title',
+ property: 'title',
+ link: constants.namingConventions.subroutes.historyQuery
+ }),
+ Ember.Object.create({
+ caption: 'columns.status',
+ property: 'status',
+ classBinding: 'status'
+ }),
+ Ember.Object.create({
+ caption: 'columns.date',
+ property: 'dateSubmitted',
+ dateRange: Ember.Object.create({
+ min: oneMonthAgo,
+ max: new Date()
+ })
+ }),
+ Ember.Object.create({
+ caption: 'columns.duration',
+ property: 'duration',
+ numberRange: Ember.Object.create({
+ min: 0,
+ max: 10,
+ units: 'sec'
+ })
+ })
+ ])}));
+ },
+
+ model: function () {
+ return this.filter(this.get('history'));
+ }.property('history', 'filters.@each'),
+
+ updateIntervals: function () {
+ var durationColumn;
+ var maxDuration;
+ var minDuration;
+
+ if (this.get('columns')) {
+ durationColumn = this.get('columns').find(function (column) {
+ return column.get('caption') === 'columns.duration';
+ });
+
+ minDuration = Math.min.apply(Math, this.get('history').map(function (item) {
+ return item.get(durationColumn.get('property'));
+ }));
+
+ maxDuration = Math.max.apply(Math, this.get('history').map(function (item) {
+ return item.get(durationColumn.get('property'));
+ }));
+
+ durationColumn.set('numberRange.min', minDuration);
+ durationColumn.set('numberRange.max', maxDuration);
+ }
+ }.observes('history'),
+
+ updateDateRange: function () {
+ var dateColumn;
+ var maxDate;
+ var minDate;
+
+ if (this.get('columns')) {
+ dateColumn = this.get('columns').find(function (column) {
+ return column.get('caption') === 'columns.date';
+ });
+
+ minDate = Math.min.apply(Math, this.get('history').map(function (item) {
+ return item.get(dateColumn.get('property'));
+ }));
+
+ maxDate = Math.max.apply(Math, this.get('history').map(function (item) {
+ return item.get(dateColumn.get('property'));
+ }));
+
+ dateColumn.set('dateRange.min', minDate);
+ dateColumn.set('dateRange.max', maxDate);
+ }
+ }.observes('history'),
+
+ filterBy: function (filterProperty, filterValue, exactMatch) {
+ var column = this.get('columns').find(function (column) {
+ return column.get('property') === filterProperty;
+ });
+
+ if (column) {
+ column.set('filterValue', filterValue, exactMatch);
+ } else {
+ this.updateFilters(filterProperty, filterValue, exactMatch);
+ }
+ },
+
+ actions: {
+ sort: function (property) {
+ //if same column has been selected, toggle flag, else default it to true
+ if (this.get('sortProperties').objectAt(0) === property) {
+ this.set('sortAscending', !this.get('sortAscending'));
+ } else {
+ this.set('sortAscending', true);
+ this.set('sortProperties', [ property ]);
+ }
+ },
+
+ interruptJob: function (job) {
+ var self = this,
+ id = job.get('id');
+
+ job.destroyRecord().then(function () {
+ self.store.find(constants.namingConventions.job, id);
+ });
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js
new file mode 100644
index 0000000..6f93b11
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/controllers/index.js
@@ -0,0 +1,475 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Controller.extend({
+ needs: [ constants.namingConventions.openQueries,
+ constants.namingConventions.databases,
+ constants.namingConventions.udfs,
+ constants.namingConventions.jobLogs,
+ constants.namingConventions.jobResults,
+ constants.namingConventions.jobExplain,
+ constants.namingConventions.settings
+ ],
+
+ openQueries: Ember.computed.alias('controllers.' + constants.namingConventions.openQueries),
+ databases: Ember.computed.alias('controllers.' + constants.namingConventions.databases),
+ udfs: Ember.computed.alias('controllers.' + constants.namingConventions.udfs + '.udfs'),
+ logs: Ember.computed.alias('controllers.' + constants.namingConventions.jobLogs),
+ results: Ember.computed.alias('controllers.' + constants.namingConventions.jobResults),
+ explain: Ember.computed.alias('controllers.' + constants.namingConventions.jobExplain),
+ settings: Ember.computed.alias('controllers.' + constants.namingConventions.settings),
+
+ canExecute: function () {
+ var isModelRunning = this.get('model.isRunning');
+ var hasParams = this.get('queryParams.length');
+
+ if (isModelRunning) {
+ return false;
+ }
+
+ if (hasParams) {
+ // all param have values?
+ return this.get('queryParams').every(function (param) { return param.value; });
+ }
+
+ return true;
+ }.property('model.isRunning', 'queryParams.@each.value'),
+
+ parseQueryParams: function () {
+ var query = this.get('openQueries.currentQuery.fileContent'),
+ param,
+ updatedParams = [],
+ currentParams = this.get('queryParams'),
+ paramRegExp = /\$\w+/ig,
+ paramNames = query.match(paramRegExp) || [];
+
+ paramNames = paramNames.uniq();
+
+ paramNames.forEach(function (name) {
+ param = currentParams.findBy('name', name);
+ if (param) {
+ updatedParams.push(param);
+ } else {
+ updatedParams.push({ name: name, value: "" });
+ }
+ });
+
+ currentParams.setObjects(updatedParams);
+ }.observes('openQueries.currentQuery.fileContent'),
+
+ _executeQuery: function (shouldExplain) {
+ var queryId,
+ self = this,
+ query,
+ finalQuery,
+ job,
+ originalModel = this.get('model');
+
+ job = this.store.createRecord(constants.namingConventions.job, {
+ title: originalModel.get('title'),
+ dataBase: this.get('databases.selectedDatabase.name')
+ });
+
+ originalModel.set('isRunning', true);
+
+ //if it's a saved query / history entry set the queryId
+ if (!originalModel.get('isNew')) {
+ queryId = originalModel.get('constructor.typeKey') === constants.namingConventions.job ?
+ originalModel.get('queryId') :
+ originalModel.get('id');
+
+ job.set('queryId', queryId);
+ }
+
+ query = this.get('openQueries').getQueryForModel(originalModel);
+
+ finalQuery = this.buildQuery(query, shouldExplain);
+ finalQuery = this.bindQueryParams(finalQuery);
+ finalQuery = this.prependQuerySettings(finalQuery);
+
+ job.set('forcedContent', finalQuery);
+
+ return this.saveQuery(job, originalModel);
+ },
+
+ saveQuery: function (job, originalModel) {
+ var defer = Ember.RSVP.defer(),
+ self = this,
+ openQueries = this.get('openQueries');
+
+ var handleError = function (err) {
+ originalModel.set('isRunning', undefined);
+ defer.reject(err);
+ };
+
+ job.save().then(function () {
+ //convert tab for current model since the execution will create a new job, and navigate to the new job route.
+ openQueries.convertTabToJob(originalModel, job).then(function () {
+ //reset flag on the original model
+ originalModel.set('isRunning', undefined);
+
+ defer.resolve(job);
+ }, function (err) {
+ handleError(err);
+ });
+
+ self.get('settings').updateSettingsId(originalModel.get('id'), job.get('id'));
+ }, function (err) {
+ handleError(err);
+ });
+
+ return defer.promise;
+ },
+
+ prependQuerySettings: function (query) {
+ var settings = this.get('settings').getSettingsString();
+
+ if (settings.length) {
+ return settings + "\n\n" + query;
+ }
+
+ return query;
+ },
+
+ buildQuery: function (query, shouldExplain) {
+ var selections = this.get('openQueries.highlightedText'),
+ isQuerySelected = selections && selections[0] !== "",
+ queryComponents = this.extractComponents(query.get('fileContent')),
+ finalQuery = '',
+ queries;
+
+ if (isQuerySelected) {
+ queries = selections.map(function (s) {
+ return s.replace(";", "");
+ });
+ } else {
+ queries = queryComponents.queryString.split(';');
+ queries = queries.filter(Boolean);
+ }
+
+ queries = queries.map(function (query) {
+ var explainIndex = query.indexOf(constants.namingConventions.explainPrefix);
+
+ if (shouldExplain) {
+ if (query.indexOf(constants.namingConventions.explainPrefix) === -1) {
+ return constants.namingConventions.explainPrefix + query;
+ }
+
+ return query;
+ } else {
+ if (query.indexOf(constants.namingConventions.explainPrefix) > -1) {
+ return query.replace(constants.namingConventions.explainPrefix, '');
+ }
+
+ return query;
+ }
+ });
+
+ if (queryComponents.files.length) {
+ finalQuery += queryComponents.files.join("\n") + "\n\n";
+ }
+
+ if (queryComponents.udfs.length) {
+ finalQuery += queryComponents.udfs.join("\n") + "\n\n";
+ }
+
+ finalQuery += queries.join(";");
+ return finalQuery;
+ },
+
+ bindQueryParams: function (query) {
+ var params = this.get('queryParams');
+
+ if (!params.get('length')) {
+ return query;
+ }
+
+ params.forEach(function (param) {
+ query = query.split(param.name).join(param.value);
+ });
+
+ return query;
+ },
+
+ init: function () {
+ this._super();
+
+ // initialize queryParams with an empty array
+ this.set('queryParams', Ember.ArrayProxy.create({ content: Ember.A([]) }))
+
+ this.set('queryProcessTabs', Ember.ArrayProxy.create({ content: Ember.A([
+ Ember.Object.create({
+ name: Ember.I18n.t('menus.logs'),
+ path: constants.namingConventions.subroutes.jobLogs
+ }),
+ Ember.Object.create({
+ name: Ember.I18n.t('menus.results'),
+ path: constants.namingConventions.subroutes.jobResults
+ }),
+ Ember.Object.create({
+ name: Ember.I18n.t('menus.explain'),
+ path: constants.namingConventions.subroutes.jobExplain
+ })
+ ])}));
+ },
+
+ displayJobTabs: function () {
+ return this.get('content.constructor.typeKey') === constants.namingConventions.job;
+ }.property('content'),
+
+ modelChanged: function () {
+ var self = this;
+ var content = this.get('content');
+ var openQueries = this.get('openQueries');
+
+ //update open queries list when current query model changes
+ openQueries.update(content).then(function (isExplainedQuery) {
+ var newId = content.get('id');
+ var tab = openQueries.getTabForModel(content);
+
+ if (content.get('constructor.typeKey') === constants.namingConventions.job) {
+ self.get('queryProcessTabs').forEach(function (queryTab) {
+ queryTab.set('id', newId);
+ });
+
+ if (isExplainedQuery) {
+ self.set('explain.content', content);
+ } else {
+ self.set('logs.content', content);
+ self.set('results.content', content);
+ }
+
+ self.setExplainVisibility(isExplainedQuery);
+
+ self.transitionToRoute(tab.get('subroute'));
+ }
+ });
+ }.observes('content'),
+
+ csvUrl: function () {
+ if (this.get('content.constructor.typeKey') !== constants.namingConventions.job) {
+ return;
+ }
+
+ if (this.get('content.status') !== constants.statuses.finished) {
+ return;
+ }
+
+ var url = this.container.lookup('adapter:application').buildURL();
+ url += '/' + constants.namingConventions.jobs + '/' + this.get('content.id');
+ url += '/results/csv';
+
+ return url;
+ }.property('content'),
+
+ downloadMenu: function () {
+ var items = [];
+ var tabs = this.get('queryProcessTabs');
+ var isResultsTabVisible = tabs.findBy('path', constants.namingConventions.subroutes.jobResults).get('visible');
+
+ if (this.get('content.status') === constants.statuses.finished && isResultsTabVisible) {
+ items.push({
+ title: Ember.I18n.t('buttons.saveHdfs'),
+ action: 'saveToHDFS'
+ });
+
+ if (this.get('csvUrl')) {
+ items.push(
+ Ember.Object.create({
+ title: Ember.I18n.t('buttons.saveCsv'),
+ href: this.get('csvUrl')
+ })
+ );
+ }
+ }
+
+ return items.length ? items : null;
+ }.property('content.status', 'queryProcessTabs.@each.visible'),
+
+ extractComponents: function (queryString) {
+ var components = {};
+
+ var udfRegEx = new RegExp("(" + constants.namingConventions.udfInsertPrefix + ").+", "ig");
+ var fileRegEx = new RegExp("(" + constants.namingConventions.fileInsertPrefix + ").+", "ig");
+
+ components.udfs = queryString.match(udfRegEx) || [];
+ components.files = queryString.match(fileRegEx) || [];
+ components.queryString = queryString.replace(udfRegEx, "").replace(fileRegEx, "").trim();
+
+ return components;
+ },
+
+ saveToHDFS: function () {
+ var job = this.get('content');
+
+ if (job.get('status') !== constants.statuses.finished) {
+ return;
+ }
+
+ var self = this;
+
+ var file = "/tmp/" + job.get('id') + ".csv";
+ var url = this.container.lookup('adapter:application').buildURL();
+ url += "/jobs/" + job.get('id') + "/results/csv/saveToHDFS";
+
+ Ember.$.getJSON(url, {
+ commence: true,
+ file: file
+ }).then(function (response) {
+ self.pollSaveToHDFS(response);
+ }, function (response) {
+ self.send('addAlert', constants.alerts.error, response.message, "alerts.errors.save.results");
+ });
+ },
+
+ pollSaveToHDFS: function (data) {
+ var self = this;
+ var url = this.container.lookup('adapter:application').buildURL();
+ url += "/jobs/" + data.jobId + "/results/csv/saveToHDFS";
+
+ Ember.run.later(function () {
+ Ember.$.getJSON(url).then(function (response) {
+ if (response.status !== constants.results.statuses.terminated) {
+ self.pollSaveToHDFS(response);
+ } else {
+ self.set('content.isRunning', false);
+ }
+ }, function (response) {
+ self.send('addAlert', constants.alerts.error, response.message, "alerts.errors.save.results");
+ });
+ }, 2000);
+ },
+
+ setExplainVisibility: function (show) {
+ var tabs = this.get('queryProcessTabs');
+
+ tabs.findBy('path', constants.namingConventions.subroutes.jobExplain).set('visible', show);
+ tabs.findBy('path', constants.namingConventions.subroutes.jobLogs).set('visible', !show);
+ tabs.findBy('path', constants.namingConventions.subroutes.jobResults).set('visible', !show);
+ },
+
+ actions: {
+ saveToHDFS: function () {
+ this.set('content.isRunning', true);
+ this.saveToHDFS();
+ },
+
+ insertUdf: function (item) {
+ var query = this.get('openQueries').getQueryForModel(this.get('model'));
+
+ var queryString = query.get('fileContent');
+
+ var newUdf = constants.namingConventions.udfInsertPrefix + item.get('name') + " as '" + item.get('classname') + "';";
+ var newFileResource = item.get('fileResource.path');
+
+ if (item.get('fileResource.path')) {
+ newFileResource = constants.namingConventions.fileInsertPrefix + item.get('fileResource.path') + ";";
+ }
+
+ var components = this.extractComponents(queryString);
+
+ if (!components.files.contains(newFileResource) && newFileResource) {
+ components.files.push(newFileResource);
+ }
+
+ if (!components.udfs.contains(newUdf)) {
+ components.udfs.push(newUdf);
+ }
+
+ var updatedContent = components.files.join("\n") + "\n\n";
+ updatedContent += components.udfs.join("\n") + "\n\n";
+ updatedContent += components.queryString;
+
+ query.set('fileContent', updatedContent);
+ },
+
+ addQuery: (function () {
+ var idCounter = 0;
+
+ return function () {
+ var model = this.store.createRecord(constants.namingConventions.savedQuery, {
+ dataBase: this.get('databases.selectedDatabase.name'),
+ title: 'New Query',
+ type: constants.namingConventions.savedQuery,
+ queryFile: '',
+ id: 'fixture_' + idCounter
+ });
+
+ idCounter++;
+
+ this.transitionToRoute(constants.namingConventions.subroutes.savedQuery, model);
+ };
+ }()),
+
+ saveQuery: function () {
+ var self = this,
+ wasNew = this.get('model.isNew'),
+ defer = Ember.RSVP.defer();
+
+ this.send('openModal', 'modal-save', {
+ heading: "modals.save.heading",
+ text: this.get('content.title'),
+ defer: defer
+ });
+
+ defer.promise.then(function (text) {
+ self.get('content').set('title', text);
+
+ self.get('openQueries').save(self.get('content')).then(function () {
+ if (wasNew) {
+ self.transitionToRoute(constants.namingConventions.subroutes.savedQuery, self.get('model.id'));
+ }
+ });
+ });
+ },
+
+ executeQuery: function () {
+ var self = this;
+ var subroute;
+
+ this._executeQuery().then(function (job) {
+ if (job.get('status') !== constants.statuses.finished) {
+ subroute = constants.namingConventions.subroutes.jobLogs;
+ } else {
+ subroute = constants.namingConventions.subroutes.jobResults;
+ }
+
+ self.get('openQueries').updateTabSubroute(job, subroute);
+
+ self.transitionToRoute(constants.namingConventions.subroutes.historyQuery, job.get('id'));
+ }, function (err) {
+ self.send('addAlert', constants.alerts.error, err.responseText, "alerts.errors.save.query");
+ });
+ },
+
+ explainQuery: function () {
+ var self = this;
+
+ this._executeQuery(true).then(function (job) {
+ self.get('openQueries').updateTabSubroute(job, constants.namingConventions.subroutes.jobExplain);
+
+ self.transitionToRoute(constants.namingConventions.subroutes.historyQuery, job.get('id'));
+ }, function (err) {
+ self.send('addAlert', constants.alerts.error, err.responseText, "alerts.errors.save.query");
+ });
+ }
+ }
+});
[02/12] ambari git commit: AMBARI-9676. Initial Hive View Submission
to Contrib (alexantonenko)
Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/codemirror.css
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/codemirror.css b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/codemirror.css
new file mode 100644
index 0000000..68c67b1
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/codemirror.css
@@ -0,0 +1,309 @@
+/* BASICS */
+
+.CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-family: monospace;
+ height: 300px;
+}
+.CodeMirror-scroll {
+ /* Set scrolling behaviour here */
+ overflow: auto;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+ padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre {
+ padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+ border-right: 1px solid #ddd;
+ background-color: #f7f7f7;
+ white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+ padding: 0 3px 0 5px;
+ min-width: 20px;
+ text-align: right;
+ color: #999;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror div.CodeMirror-cursor {
+ border-left: 1px solid black;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+ border-left: 1px solid silver;
+}
+.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
+ width: auto;
+ border: 0;
+ background: #7e7;
+}
+.CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursors {
+ z-index: 1;
+}
+
+.cm-animate-fat-cursor {
+ width: auto;
+ border: 0;
+ -webkit-animation: blink 1.06s steps(1) infinite;
+ -moz-animation: blink 1.06s steps(1) infinite;
+ animation: blink 1.06s steps(1) infinite;
+}
+@-moz-keyframes blink {
+ 0% { background: #7e7; }
+ 50% { background: none; }
+ 100% { background: #7e7; }
+}
+@-webkit-keyframes blink {
+ 0% { background: #7e7; }
+ 50% { background: none; }
+ 100% { background: #7e7; }
+}
+@keyframes blink {
+ 0% { background: #7e7; }
+ 50% { background: none; }
+ 100% { background: #7e7; }
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+div.CodeMirror-overwrite div.CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-ruler {
+ border-left: 1px solid #ccc;
+ position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+ the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+ line-height: 1;
+ position: relative;
+ overflow: hidden;
+ background: white;
+ color: black;
+}
+
+.CodeMirror-scroll {
+ /* 30px is the magic margin used to hide the element's real scrollbars */
+ /* See overflow: hidden in .CodeMirror */
+ margin-bottom: -30px; margin-right: -30px;
+ padding-bottom: 30px;
+ height: 100%;
+ outline: none; /* Prevent dragging from highlighting the element */
+ position: relative;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+.CodeMirror-sizer {
+ position: relative;
+ border-right: 30px solid transparent;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+ before actuall scrolling happens, thus preventing shaking and
+ flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ position: absolute;
+ z-index: 6;
+ display: none;
+}
+.CodeMirror-vscrollbar {
+ right: 0; top: 0;
+ overflow-x: hidden;
+ overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+ bottom: 0; left: 0;
+ overflow-y: hidden;
+ overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+ right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+ left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+ position: absolute; left: 0; top: 0;
+ padding-bottom: 30px;
+ z-index: 3;
+}
+.CodeMirror-gutter {
+ white-space: normal;
+ height: 100%;
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ padding-bottom: 30px;
+ margin-bottom: -32px;
+ display: inline-block;
+ /* Hack to make IE7 behave */
+ *zoom:1;
+ *display:inline;
+}
+.CodeMirror-gutter-elt {
+ position: absolute;
+ cursor: default;
+ z-index: 4;
+}
+
+.CodeMirror-lines {
+ cursor: text;
+ min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre {
+ /* Reset some styles that the rest of the page might have set */
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+ border-width: 0;
+ background: transparent;
+ font-family: inherit;
+ font-size: inherit;
+ margin: 0;
+ white-space: pre;
+ word-wrap: normal;
+ line-height: inherit;
+ color: inherit;
+ z-index: 2;
+ position: relative;
+ overflow: visible;
+}
+.CodeMirror-wrap pre {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: normal;
+}
+
+.CodeMirror-linebackground {
+ position: absolute;
+ left: 0; right: 0; top: 0; bottom: 0;
+ z-index: 0;
+}
+
+.CodeMirror-linewidget {
+ position: relative;
+ z-index: 2;
+ overflow: auto;
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-wrap .CodeMirror-scroll {
+ overflow-x: hidden;
+}
+
+.CodeMirror-measure {
+ position: absolute;
+ width: 100%;
+ height: 0;
+ overflow: hidden;
+ visibility: hidden;
+}
+.CodeMirror-measure pre { position: static; }
+
+.CodeMirror div.CodeMirror-cursor {
+ position: absolute;
+ border-right: none;
+ width: 0;
+}
+
+div.CodeMirror-cursors {
+ visibility: hidden;
+ position: relative;
+ z-index: 3;
+}
+.CodeMirror-focused div.CodeMirror-cursors {
+ visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+
+.cm-searching {
+ background: #ffa;
+ background: rgba(255, 255, 0, .4);
+}
+
+/* IE7 hack to prevent it from returning funny offsetTops on the spans */
+.CodeMirror span { *vertical-align: text-bottom; }
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+ /* Hide the cursor when printing */
+ .CodeMirror div.CodeMirror-cursors {
+ visibility: hidden;
+ }
+}
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/show-hint.css
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/show-hint.css b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/show-hint.css
new file mode 100644
index 0000000..924e638
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/show-hint.css
@@ -0,0 +1,38 @@
+.CodeMirror-hints {
+ position: absolute;
+ z-index: 10;
+ overflow: hidden;
+ list-style: none;
+
+ margin: 0;
+ padding: 2px;
+
+ -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+ -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+ box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+ border-radius: 3px;
+ border: 1px solid silver;
+
+ background: white;
+ font-size: 90%;
+ font-family: monospace;
+
+ max-height: 20em;
+ overflow-y: auto;
+}
+
+.CodeMirror-hint {
+ margin: 0;
+ padding: 0 4px;
+ border-radius: 2px;
+ max-width: 19em;
+ overflow: hidden;
+ white-space: pre;
+ color: black;
+ cursor: pointer;
+}
+
+li.CodeMirror-hint-active {
+ background: #08f;
+ color: white;
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/show-hint.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/show-hint.js b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/show-hint.js
new file mode 100644
index 0000000..27b770b
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/show-hint.js
@@ -0,0 +1,389 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var HINT_ELEMENT_CLASS = "CodeMirror-hint";
+ var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
+
+ // This is the old interface, kept around for now to stay
+ // backwards-compatible.
+ CodeMirror.showHint = function(cm, getHints, options) {
+ if (!getHints) return cm.showHint(options);
+ if (options && options.async) getHints.async = true;
+ var newOpts = {hint: getHints};
+ if (options) for (var prop in options) newOpts[prop] = options[prop];
+ return cm.showHint(newOpts);
+ };
+
+ CodeMirror.defineExtension("showHint", function(options) {
+ // We want a single cursor position.
+ if (this.listSelections().length > 1 || this.somethingSelected()) return;
+
+ if (this.state.completionActive) this.state.completionActive.close();
+ var completion = this.state.completionActive = new Completion(this, options);
+ var getHints = completion.options.hint;
+ if (!getHints) return;
+
+ CodeMirror.signal(this, "startCompletion", this);
+ if (getHints.async)
+ getHints(this, function(hints) { completion.showHints(hints); }, completion.options);
+ else
+ return completion.showHints(getHints(this, completion.options));
+ });
+
+ function Completion(cm, options) {
+ this.cm = cm;
+ this.options = this.buildOptions(options);
+ this.widget = this.onClose = null;
+ }
+
+ Completion.prototype = {
+ close: function() {
+ if (!this.active()) return;
+ this.cm.state.completionActive = null;
+
+ if (this.widget) this.widget.close();
+ if (this.onClose) this.onClose();
+ CodeMirror.signal(this.cm, "endCompletion", this.cm);
+ },
+
+ active: function() {
+ return this.cm.state.completionActive == this;
+ },
+
+ pick: function(data, i) {
+ var completion = data.list[i];
+ if (completion.hint) completion.hint(this.cm, data, completion);
+ else this.cm.replaceRange(getText(completion), completion.from || data.from,
+ completion.to || data.to, "complete");
+ CodeMirror.signal(data, "pick", completion);
+ this.close();
+ },
+
+ showHints: function(data) {
+ if (!data || !data.list.length || !this.active()) return this.close();
+
+ if (this.options.completeSingle && data.list.length == 1)
+ this.pick(data, 0);
+ else
+ this.showWidget(data);
+ },
+
+ showWidget: function(data) {
+ this.widget = new Widget(this, data);
+ CodeMirror.signal(data, "shown");
+
+ var debounce = 0, completion = this, finished;
+ var closeOn = this.options.closeCharacters;
+ var startPos = this.cm.getCursor(), startLen = this.cm.getLine(startPos.line).length;
+
+ var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
+ return setTimeout(fn, 1000/60);
+ };
+ var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
+
+ function done() {
+ if (finished) return;
+ finished = true;
+ completion.close();
+ completion.cm.off("cursorActivity", activity);
+ if (data) CodeMirror.signal(data, "close");
+ }
+
+ function update() {
+ if (finished) return;
+ CodeMirror.signal(data, "update");
+ var getHints = completion.options.hint;
+ if (getHints.async)
+ getHints(completion.cm, finishUpdate, completion.options);
+ else
+ finishUpdate(getHints(completion.cm, completion.options));
+ }
+ function finishUpdate(data_) {
+ data = data_;
+ if (finished) return;
+ if (!data || !data.list.length) return done();
+ if (completion.widget) completion.widget.close();
+ completion.widget = new Widget(completion, data);
+ }
+
+ function clearDebounce() {
+ if (debounce) {
+ cancelAnimationFrame(debounce);
+ debounce = 0;
+ }
+ }
+
+ function activity() {
+ clearDebounce();
+ var pos = completion.cm.getCursor(), line = completion.cm.getLine(pos.line);
+ if (pos.line != startPos.line || line.length - pos.ch != startLen - startPos.ch ||
+ pos.ch < startPos.ch || completion.cm.somethingSelected() ||
+ (pos.ch && closeOn.test(line.charAt(pos.ch - 1)))) {
+ completion.close();
+ } else {
+ debounce = requestAnimationFrame(update);
+ if (completion.widget) completion.widget.close();
+ }
+ }
+ this.cm.on("cursorActivity", activity);
+ this.onClose = done;
+ },
+
+ buildOptions: function(options) {
+ var editor = this.cm.options.hintOptions;
+ var out = {};
+ for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
+ if (editor) for (var prop in editor)
+ if (editor[prop] !== undefined) out[prop] = editor[prop];
+ if (options) for (var prop in options)
+ if (options[prop] !== undefined) out[prop] = options[prop];
+ return out;
+ }
+ };
+
+ function getText(completion) {
+ if (typeof completion == "string") return completion;
+ else return completion.text;
+ }
+
+ function buildKeyMap(completion, handle) {
+ var baseMap = {
+ Up: function() {handle.moveFocus(-1);},
+ Down: function() {handle.moveFocus(1);},
+ PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
+ PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
+ Home: function() {handle.setFocus(0);},
+ End: function() {handle.setFocus(handle.length - 1);},
+ Enter: handle.pick,
+ Tab: handle.pick,
+ Esc: handle.close
+ };
+ var custom = completion.options.customKeys;
+ var ourMap = custom ? {} : baseMap;
+ function addBinding(key, val) {
+ var bound;
+ if (typeof val != "string")
+ bound = function(cm) { return val(cm, handle); };
+ // This mechanism is deprecated
+ else if (baseMap.hasOwnProperty(val))
+ bound = baseMap[val];
+ else
+ bound = val;
+ ourMap[key] = bound;
+ }
+ if (custom)
+ for (var key in custom) if (custom.hasOwnProperty(key))
+ addBinding(key, custom[key]);
+ var extra = completion.options.extraKeys;
+ if (extra)
+ for (var key in extra) if (extra.hasOwnProperty(key))
+ addBinding(key, extra[key]);
+ return ourMap;
+ }
+
+ function getHintElement(hintsElement, el) {
+ while (el && el != hintsElement) {
+ if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
+ el = el.parentNode;
+ }
+ }
+
+ function Widget(completion, data) {
+ this.completion = completion;
+ this.data = data;
+ var widget = this, cm = completion.cm;
+
+ var hints = this.hints = document.createElement("ul");
+ hints.className = "CodeMirror-hints";
+ this.selectedHint = data.selectedHint || 0;
+
+ var completions = data.list;
+ for (var i = 0; i < completions.length; ++i) {
+ var elt = hints.appendChild(document.createElement("li")), cur = completions[i];
+ var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
+ if (cur.className != null) className = cur.className + " " + className;
+ elt.className = className;
+ if (cur.render) cur.render(elt, data, cur);
+ else elt.appendChild(document.createTextNode(cur.displayText || getText(cur)));
+ elt.hintId = i;
+ }
+
+ var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
+ var left = pos.left, top = pos.bottom, below = true;
+ hints.style.left = left + "px";
+ hints.style.top = top + "px";
+ // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
+ var winW = window.innerWidth || Math.max(document.body.offsetWidth, document.documentElement.offsetWidth);
+ var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
+ (completion.options.container || document.body).appendChild(hints);
+ var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
+ if (overlapY > 0) {
+ var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
+ if (curTop - height > 0) { // Fits above cursor
+ hints.style.top = (top = pos.top - height) + "px";
+ below = false;
+ } else if (height > winH) {
+ hints.style.height = (winH - 5) + "px";
+ hints.style.top = (top = pos.bottom - box.top) + "px";
+ var cursor = cm.getCursor();
+ if (data.from.ch != cursor.ch) {
+ pos = cm.cursorCoords(cursor);
+ hints.style.left = (left = pos.left) + "px";
+ box = hints.getBoundingClientRect();
+ }
+ }
+ }
+ var overlapX = box.left - winW;
+ if (overlapX > 0) {
+ if (box.right - box.left > winW) {
+ hints.style.width = (winW - 5) + "px";
+ overlapX -= (box.right - box.left) - winW;
+ }
+ hints.style.left = (left = pos.left - overlapX) + "px";
+ }
+
+ cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
+ moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
+ setFocus: function(n) { widget.changeActive(n); },
+ menuSize: function() { return widget.screenAmount(); },
+ length: completions.length,
+ close: function() { completion.close(); },
+ pick: function() { widget.pick(); },
+ data: data
+ }));
+
+ if (completion.options.closeOnUnfocus) {
+ var closingOnBlur;
+ cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
+ cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
+ }
+
+ var startScroll = cm.getScrollInfo();
+ cm.on("scroll", this.onScroll = function() {
+ var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
+ var newTop = top + startScroll.top - curScroll.top;
+ var point = newTop - (window.pageYOffset || (document.documentElement || document.body).scrollTop);
+ if (!below) point += hints.offsetHeight;
+ if (point <= editor.top || point >= editor.bottom) return completion.close();
+ hints.style.top = newTop + "px";
+ hints.style.left = (left + startScroll.left - curScroll.left) + "px";
+ });
+
+ CodeMirror.on(hints, "dblclick", function(e) {
+ var t = getHintElement(hints, e.target || e.srcElement);
+ if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
+ });
+
+ CodeMirror.on(hints, "click", function(e) {
+ var t = getHintElement(hints, e.target || e.srcElement);
+ if (t && t.hintId != null) {
+ widget.changeActive(t.hintId);
+ if (completion.options.completeOnSingleClick) widget.pick();
+ }
+ });
+
+ CodeMirror.on(hints, "mousedown", function() {
+ setTimeout(function(){cm.focus();}, 20);
+ });
+
+ CodeMirror.signal(data, "select", completions[0], hints.firstChild);
+ return true;
+ }
+
+ Widget.prototype = {
+ close: function() {
+ if (this.completion.widget != this) return;
+ this.completion.widget = null;
+ this.hints.parentNode.removeChild(this.hints);
+ this.completion.cm.removeKeyMap(this.keyMap);
+
+ var cm = this.completion.cm;
+ if (this.completion.options.closeOnUnfocus) {
+ cm.off("blur", this.onBlur);
+ cm.off("focus", this.onFocus);
+ }
+ cm.off("scroll", this.onScroll);
+ },
+
+ pick: function() {
+ this.completion.pick(this.data, this.selectedHint);
+ },
+
+ changeActive: function(i, avoidWrap) {
+ if (i >= this.data.list.length)
+ i = avoidWrap ? this.data.list.length - 1 : 0;
+ else if (i < 0)
+ i = avoidWrap ? 0 : this.data.list.length - 1;
+ if (this.selectedHint == i) return;
+ var node = this.hints.childNodes[this.selectedHint];
+ node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
+ node = this.hints.childNodes[this.selectedHint = i];
+ node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
+ if (node.offsetTop < this.hints.scrollTop)
+ this.hints.scrollTop = node.offsetTop - 3;
+ else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
+ this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
+ CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
+ },
+
+ screenAmount: function() {
+ return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
+ }
+ };
+
+ CodeMirror.registerHelper("hint", "auto", function(cm, options) {
+ var helpers = cm.getHelpers(cm.getCursor(), "hint"), words;
+ if (helpers.length) {
+ for (var i = 0; i < helpers.length; i++) {
+ var cur = helpers[i](cm, options);
+ if (cur && cur.list.length) return cur;
+ }
+ } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
+ if (words) return CodeMirror.hint.fromList(cm, {words: words});
+ } else if (CodeMirror.hint.anyword) {
+ return CodeMirror.hint.anyword(cm, options);
+ }
+ });
+
+ CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
+ var cur = cm.getCursor(), token = cm.getTokenAt(cur);
+ var found = [];
+ for (var i = 0; i < options.words.length; i++) {
+ var word = options.words[i];
+ if (word.slice(0, token.string.length) == token.string)
+ found.push(word);
+ }
+
+ if (found.length) return {
+ list: found,
+ from: CodeMirror.Pos(cur.line, token.start),
+ to: CodeMirror.Pos(cur.line, token.end)
+ };
+ });
+
+ CodeMirror.commands.autocomplete = CodeMirror.showHint;
+
+ var defaultOptions = {
+ hint: CodeMirror.hint.auto,
+ completeSingle: true,
+ alignWithWord: true,
+ closeCharacters: /[\s()\[\]{};:>,]/,
+ closeOnUnfocus: true,
+ completeOnSingleClick: false,
+ container: null,
+ customKeys: null,
+ extraKeys: null
+ };
+
+ CodeMirror.defineOption("hintOptions", null);
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/sql-hint.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/sql-hint.js b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/sql-hint.js
new file mode 100644
index 0000000..522f9e8
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/codemirror/sql-hint.js
@@ -0,0 +1,192 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var tables;
+ var defaultTable;
+ var keywords;
+ var CONS = {
+ QUERY_DIV: ";",
+ ALIAS_KEYWORD: "AS"
+ };
+ var Pos = CodeMirror.Pos;
+
+ function getKeywords(editor) {
+ var mode = editor.doc.modeOption;
+ if (mode === "sql") mode = "text/x-sql";
+ return CodeMirror.resolveMode(mode).keywords;
+ }
+
+ function match(string, word) {
+ var len = string.length;
+ var sub = word.substr(0, len);
+ return string.toUpperCase() === sub.toUpperCase();
+ }
+
+ function addMatches(result, search, wordlist, formatter) {
+ for (var word in wordlist) {
+ if (!wordlist.hasOwnProperty(word)) continue;
+ if (Array.isArray(wordlist)) {
+ word = wordlist[word];
+ }
+ if (match(search, word)) {
+ result.push(formatter(word));
+ }
+ }
+ }
+
+ function nameCompletion(result, editor) {
+ var cur = editor.getCursor();
+ var token = editor.getTokenAt(cur);
+ var useBacktick = (token.string.charAt(0) == "`");
+ var string = token.string.substr(1);
+ var prevToken = editor.getTokenAt(Pos(cur.line, token.start));
+ if (token.string.charAt(0) == "." || prevToken.string == "."){
+ //Suggest colunm names
+ prevToken = prevToken.string == "." ? editor.getTokenAt(Pos(cur.line, token.start - 1)) : prevToken;
+ var table = prevToken.string;
+ //Check if backtick is used in table name. If yes, use it for columns too.
+ var useBacktickTable = false;
+ if (table.match(/`/g)) {
+ useBacktickTable = true;
+ table = table.replace(/`/g, "");
+ }
+ //Check if table is available. If not, find table by Alias
+ if (!tables.hasOwnProperty(table))
+ table = findTableByAlias(table, editor);
+ var columns = tables[table];
+ if (!columns) return;
+
+ if (useBacktick) {
+ addMatches(result, string, columns, function(w) {return "`" + w + "`";});
+ }
+ else if(useBacktickTable) {
+ addMatches(result, string, columns, function(w) {return ".`" + w + "`";});
+ }
+ else {
+ addMatches(result, string, columns, function(w) {return "." + w;});
+ }
+ }
+ else {
+ //Suggest table names or colums in defaultTable
+ while (token.start && string.charAt(0) == ".") {
+ token = editor.getTokenAt(Pos(cur.line, token.start - 1));
+ string = token.string + string;
+ }
+ if (useBacktick) {
+ addMatches(result, string, tables, function(w) {return "`" + w + "`";});
+ addMatches(result, string, defaultTable, function(w) {return "`" + w + "`";});
+ }
+ else {
+ addMatches(result, string, tables, function(w) {return w;});
+ addMatches(result, string, defaultTable, function(w) {return w;});
+ }
+ }
+ }
+
+ function eachWord(lineText, f) {
+ if (!lineText) return;
+ var excepted = /[,;]/g;
+ var words = lineText.split(" ");
+ for (var i = 0; i < words.length; i++) {
+ f(words[i]?words[i].replace(excepted, '') : '');
+ }
+ }
+
+ function convertCurToNumber(cur) {
+ // max characters of a line is 999,999.
+ return cur.line + cur.ch / Math.pow(10, 6);
+ }
+
+ function convertNumberToCur(num) {
+ return Pos(Math.floor(num), +num.toString().split('.').pop());
+ }
+
+ function findTableByAlias(alias, editor) {
+ var doc = editor.doc;
+ var fullQuery = doc.getValue();
+ var aliasUpperCase = alias.toUpperCase();
+ var previousWord = "";
+ var table = "";
+ var separator = [];
+ var validRange = {
+ start: Pos(0, 0),
+ end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
+ };
+
+ //add separator
+ var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
+ while(indexOfSeparator != -1) {
+ separator.push(doc.posFromIndex(indexOfSeparator));
+ indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
+ }
+ separator.unshift(Pos(0, 0));
+ separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
+
+ //find valid range
+ var prevItem = 0;
+ var current = convertCurToNumber(editor.getCursor());
+ for (var i=0; i< separator.length; i++) {
+ var _v = convertCurToNumber(separator[i]);
+ if (current > prevItem && current <= _v) {
+ validRange = { start: convertNumberToCur(prevItem), end: convertNumberToCur(_v) };
+ break;
+ }
+ prevItem = _v;
+ }
+
+ var query = doc.getRange(validRange.start, validRange.end, false);
+
+ for (var i = 0; i < query.length; i++) {
+ var lineText = query[i];
+ eachWord(lineText, function(word) {
+ var wordUpperCase = word.toUpperCase();
+ if (wordUpperCase === aliasUpperCase && tables.hasOwnProperty(previousWord)) {
+ table = previousWord;
+ }
+ if (wordUpperCase !== CONS.ALIAS_KEYWORD) {
+ previousWord = word;
+ }
+ });
+ if (table) break;
+ }
+ return table;
+ }
+
+ CodeMirror.registerHelper("hint", "sql", function(editor, options) {
+ tables = (options && options.tables) || {};
+ var defaultTableName = options && options.defaultTable;
+ defaultTable = (defaultTableName && tables[defaultTableName] || []);
+ keywords = keywords || getKeywords(editor);
+
+ var cur = editor.getCursor();
+ var result = [];
+ var token = editor.getTokenAt(cur), start, end, search;
+ if (token.string.match(/^[.`\w@]\w*$/)) {
+ search = token.string;
+ start = token.start;
+ end = token.end;
+ } else {
+ start = end = cur.ch;
+ search = "";
+ }
+ if (search.charAt(0) == "." || search.charAt(0) == "`") {
+ nameCompletion(result, editor);
+ } else {
+ addMatches(result, search, tables, function(w) {return w;});
+ addMatches(result, search, defaultTable, function(w) {return w;});
+ addMatches(result, search, keywords, function(w) {return w.toUpperCase();});
+ }
+
+ return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/view.xml
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/view.xml b/contrib/views/hive/src/main/resources/view.xml
new file mode 100644
index 0000000..b936c6d
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/view.xml
@@ -0,0 +1,160 @@
+<!--
+ 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.
+-->
+<view>
+ <name>HIVE</name>
+ <label>Hive</label>
+ <version>0.0.1</version>
+
+ <!-- HDFS Configs -->
+ <parameter>
+ <name>webhdfs.url</name>
+ <description>WebHDFS FileSystem URI (example: webhdfs://namenode:50070)</description>
+ <required>true</required>
+ </parameter>
+
+ <parameter>
+ <name>webhdfs.username</name>
+ <description>User and doAs for proxy user for HDFS</description>
+ <required>false</required>
+ </parameter>
+
+ <parameter>
+ <name>webhdfs.auth</name>
+ <description>Semicolon-separated authentication configs. Default: auth=SIMPLE</description>
+ <required>false</required>
+ </parameter>
+
+ <!-- General Configs -->
+
+ <parameter>
+ <name>dataworker.username</name>
+ <description>The username (defaults to ViewContext username)</description>
+ <required>false</required>
+ </parameter>
+
+ <parameter>
+ <name>scripts.dir</name>
+ <description>HDFS directory path to store Hive scripts (example: /users/${username})</description>
+ <required>true</required>
+ </parameter>
+
+ <parameter>
+ <name>jobs.dir</name>
+ <description>HDFS directory path to store Hive job status (example: /users/${username})</description>
+ <required>true</required>
+ </parameter>
+
+ <parameter>
+ <name>hive.host</name>
+ <description>HiveServer2 hostname or IP (example: 127.0.0.1)</description>
+ <required>true</required>
+ </parameter>
+
+ <parameter>
+ <name>hive.port</name>
+ <description>HiveServer2 Thrift port (example: 10000)</description>
+ <required>true</required>
+ </parameter>
+
+ <parameter>
+ <name>hive.auth</name>
+ <description>Semicolon-separated authentication configs. Default: auth=NOSASL</description>
+ <required>false</required>
+ </parameter>
+
+ <resource>
+ <name>savedQuery</name>
+ <plural-name>savedQueries</plural-name>
+ <id-property>id</id-property>
+ <resource-class>org.apache.ambari.view.hive.resources.savedQueries.SavedQuery</resource-class>
+ <provider-class>org.apache.ambari.view.hive.resources.savedQueries.SavedQueryResourceProvider</provider-class>
+ <service-class>org.apache.ambari.view.hive.resources.savedQueries.SavedQueryService</service-class>
+ </resource>
+
+ <resource>
+ <name>fileResource</name>
+ <plural-name>fileResources</plural-name>
+ <id-property>id</id-property>
+ <resource-class>org.apache.ambari.view.hive.resources.resources.FileResourceItem</resource-class>
+ <provider-class>org.apache.ambari.view.hive.resources.resources.FileResourceResourceProvider</provider-class>
+ <service-class>org.apache.ambari.view.hive.resources.resources.FileResourceService</service-class>
+ </resource>
+
+ <resource>
+ <name>udf</name>
+ <plural-name>udfs</plural-name>
+ <id-property>id</id-property>
+ <resource-class>org.apache.ambari.view.hive.resources.udfs.UDF</resource-class>
+ <provider-class>org.apache.ambari.view.hive.resources.udfs.UDFResourceProvider</provider-class>
+ <service-class>org.apache.ambari.view.hive.resources.udfs.UDFService</service-class>
+ </resource>
+
+ <resource>
+ <name>job</name>
+ <plural-name>jobs</plural-name>
+ <id-property>id</id-property>
+ <resource-class>org.apache.ambari.view.hive.resources.jobs.JobImpl</resource-class>
+ <provider-class>org.apache.ambari.view.hive.resources.jobs.JobResourceProvider</provider-class>
+ <service-class>org.apache.ambari.view.hive.resources.jobs.JobService</service-class>
+ </resource>
+
+ <resource>
+ <name>file</name>
+ <service-class>org.apache.ambari.view.hive.resources.files.FileService</service-class>
+ </resource>
+
+ <resource>
+ <name>ddl</name>
+ <service-class>org.apache.ambari.view.hive.resources.browser.HiveBrowserService</service-class>
+ </resource>
+
+ <resource>
+ <name>hive</name>
+ <service-class>org.apache.ambari.view.hive.HelpService</service-class>
+ </resource>
+
+ <persistence>
+ <entity>
+ <class>org.apache.ambari.view.hive.persistence.DataStoreStorage$SmokeTestEntity</class>
+ <id-property>id</id-property>
+ </entity>
+ <entity>
+ <class>org.apache.ambari.view.hive.resources.jobs.JobImpl</class>
+ <id-property>id</id-property>
+ </entity>
+ <entity>
+ <class>org.apache.ambari.view.hive.resources.jobs.StoredOperationHandle</class>
+ <id-property>id</id-property>
+ </entity>
+ <entity>
+ <class>org.apache.ambari.view.hive.resources.savedQueries.SavedQuery</class>
+ <id-property>id</id-property>
+ </entity>
+ <entity>
+ <class>org.apache.ambari.view.hive.resources.udfs.UDF</class>
+ <id-property>id</id-property>
+ </entity>
+ <entity>
+ <class>org.apache.ambari.view.hive.resources.resources.FileResourceItem</class>
+ <id-property>id</id-property>
+ </entity>
+ <entity>
+ <class>org.apache.ambari.view.hive.TestBean</class>
+ <id-property>id</id-property>
+ </entity>
+ </persistence>
+</view>
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/BaseHiveTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/BaseHiveTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/BaseHiveTest.java
new file mode 100644
index 0000000..f38d0e9
--- /dev/null
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/BaseHiveTest.java
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.ViewResourceHandler;
+import org.apache.hadoop.fs.FileUtil;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.easymock.EasyMock.*;
+
+public abstract class BaseHiveTest {
+ protected ViewResourceHandler handler;
+ protected ViewContext context;
+ protected static File hiveStorageFile;
+ protected static File baseDir;
+ protected Map<String, String> properties;
+
+ protected static String DATA_DIRECTORY = "./target/HiveTest";
+
+ @BeforeClass
+ public static void startUp() throws Exception {
+ File baseDir = new File(DATA_DIRECTORY)
+ .getAbsoluteFile();
+ FileUtil.fullyDelete(baseDir);
+ }
+
+ @AfterClass
+ public static void shutDown() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ handler = createNiceMock(ViewResourceHandler.class);
+ context = createNiceMock(ViewContext.class);
+
+ properties = new HashMap<String, String>();
+ baseDir = new File(DATA_DIRECTORY)
+ .getAbsoluteFile();
+ hiveStorageFile = new File("./target/HiveTest/storage.dat")
+ .getAbsoluteFile();
+
+ properties.put("dataworker.storagePath", hiveStorageFile.toString());
+ properties.put("scripts.dir", "/tmp/.hiveQueries");
+ properties.put("jobs.dir", "/tmp/.hiveJobs");
+
+ setupProperties(properties, baseDir);
+
+ expect(context.getProperties()).andReturn(properties).anyTimes();
+ expect(context.getUsername()).andReturn("ambari-qa").anyTimes();
+ expect(context.getInstanceName()).andReturn("MyHive").anyTimes();
+
+ replay(handler, context);
+ }
+
+ protected void setupProperties(Map<String, String> properties, File baseDir) throws Exception {
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+
+ }
+
+ protected static <T> T getService(Class<T> clazz,
+ final ViewResourceHandler viewResourceHandler,
+ final ViewContext viewInstanceContext) {
+ Injector viewInstanceInjector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(ViewResourceHandler.class).toInstance(viewResourceHandler);
+ bind(ViewContext.class).toInstance(viewInstanceContext);
+ }
+ });
+ return viewInstanceInjector.getInstance(clazz);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/HDFSTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/HDFSTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/HDFSTest.java
new file mode 100644
index 0000000..0ee8eb3
--- /dev/null
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/HDFSTest.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileUtil;
+import org.apache.hadoop.hdfs.MiniDFSCluster;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import java.io.File;
+import java.util.Map;
+
+public abstract class HDFSTest extends BaseHiveTest {
+ protected static MiniDFSCluster hdfsCluster;
+ protected static String hdfsURI;
+
+ @BeforeClass
+ public static void startUp() throws Exception {
+ BaseHiveTest.startUp(); // super
+ File hdfsDir = new File("./target/HiveTest/hdfs/")
+ .getAbsoluteFile();
+ FileUtil.fullyDelete(hdfsDir);
+
+ Configuration conf = new Configuration();
+ conf.set(MiniDFSCluster.HDFS_MINIDFS_BASEDIR, hdfsDir.getAbsolutePath());
+ conf.set("hadoop.proxyuser." + System.getProperty("user.name") + ".groups", "*");
+ conf.set("hadoop.proxyuser." + System.getProperty("user.name") + ".hosts", "*");
+
+ MiniDFSCluster.Builder builder = new MiniDFSCluster.Builder(conf);
+ hdfsCluster = builder.build();
+ hdfsURI = hdfsCluster.getURI().toString();
+ }
+
+ @AfterClass
+ public static void shutDown() throws Exception {
+ BaseHiveTest.shutDown();
+ hdfsCluster.shutdown();
+ hdfsCluster = null;
+ }
+
+ @Override
+ protected void setupProperties(Map<String, String> properties, File baseDir) throws Exception {
+ super.setupProperties(properties, baseDir);
+ properties.put("webhdfs.url", hdfsURI);
+ properties.put("webhdfs.username", System.getProperty("user.name"));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/ServiceTestUtils.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/ServiceTestUtils.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/ServiceTestUtils.java
new file mode 100644
index 0000000..ac913a9
--- /dev/null
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/ServiceTestUtils.java
@@ -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.
+ */
+
+package org.apache.ambari.view.hive;
+
+import org.junit.Assert;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+
+import java.net.URI;
+
+import static org.easymock.EasyMock.*;
+
+public class ServiceTestUtils {
+ public static void assertHTTPResponseOK(Response response) {
+ Assert.assertEquals(200, response.getStatus());
+ }
+
+ public static void assertHTTPResponseCreated(Response response) {
+ Assert.assertEquals(201, response.getStatus());
+ }
+
+ public static void assertHTTPResponseNoContent(Response response) {
+ Assert.assertEquals(204, response.getStatus());
+ }
+
+ public static void expectLocationHeaderInResponse(HttpServletResponse resp_obj) {
+ resp_obj.setHeader(eq("Location"), anyString());
+ }
+
+ public static UriInfo getDefaultUriInfo() {
+ UriInfo uriInfo = createNiceMock(UriInfo.class);
+ URI uri = UriBuilder.fromUri("http://host/a/b").build();
+ expect(uriInfo.getAbsolutePath()).andReturn(uri);
+ replay(uriInfo);
+ return uriInfo;
+ }
+
+ public static HttpServletResponse getResponseWithLocation() {
+ HttpServletResponse resp_obj = createNiceMock(HttpServletResponse.class);
+ expectLocationHeaderInResponse(resp_obj);
+ replay(resp_obj);
+ return resp_obj;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/backgroundjobs/BackgroundJobControllerTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/backgroundjobs/BackgroundJobControllerTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/backgroundjobs/BackgroundJobControllerTest.java
new file mode 100644
index 0000000..ceb3677
--- /dev/null
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/backgroundjobs/BackgroundJobControllerTest.java
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.backgroundjobs;
+
+import org.apache.ambari.view.hive.BaseHiveTest;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class BackgroundJobControllerTest extends BaseHiveTest {
+
+ private static final long MAX_WAIT_TIME = 2000;
+
+ @Test
+ public void testStartJob() throws Exception {
+ BackgroundJobController backgroundJobController = new BackgroundJobController(context);
+
+ HangingRunnable runnable = new HangingRunnable();
+ backgroundJobController.startJob("key", runnable);
+
+ assertStateIs(backgroundJobController, "key", Thread.State.RUNNABLE);
+
+ runnable.goOn();
+ assertStateIs(backgroundJobController, "key", Thread.State.TERMINATED);
+ }
+
+ @Test
+ public void testInterrupt() throws Exception {
+ BackgroundJobController backgroundJobController = new BackgroundJobController(context);
+
+ HangingRunnable runnable = new HangingRunnable();
+ backgroundJobController.startJob("key", runnable);
+
+ assertStateIs(backgroundJobController, "key", Thread.State.RUNNABLE);
+
+ backgroundJobController.interrupt("key");
+ assertStateIs(backgroundJobController, "key", Thread.State.TERMINATED);
+ }
+
+ private void assertStateIs(BackgroundJobController backgroundJobController, String key, Thread.State state) throws InterruptedException {
+ long start = System.currentTimeMillis();
+ while (backgroundJobController.state(key) != state) {
+ Thread.sleep(100);
+ if (System.currentTimeMillis() - start > MAX_WAIT_TIME)
+ break;
+ }
+ Assert.assertEquals(state, backgroundJobController.state(key));
+ }
+
+ private static class HangingRunnable implements Runnable {
+ private boolean waitMe = true;
+
+ @Override
+ public void run() {
+ while(waitMe && !Thread.interrupted());
+ }
+
+ public void goOn() {
+ this.waitMe = false;
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/files/FileServiceTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/files/FileServiceTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/files/FileServiceTest.java
new file mode 100644
index 0000000..7ce2dd3
--- /dev/null
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/files/FileServiceTest.java
@@ -0,0 +1,214 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.files;
+
+import org.apache.ambari.view.hive.ServiceTestUtils;
+import org.apache.ambari.view.hive.HDFSTest;
+import org.apache.ambari.view.hive.utils.*;
+import org.apache.ambari.view.hive.utils.HdfsApi;
+import org.apache.hadoop.fs.FSDataInputStream;
+import org.apache.hadoop.fs.Path;
+import org.json.simple.JSONObject;
+import org.junit.*;
+import org.junit.rules.ExpectedException;
+
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+public class FileServiceTest extends HDFSTest {
+ private final static int PAGINATOR_PAGE_SIZE = 4; //4 bytes
+ private FileService fileService;
+
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ fileService = getService(FileService.class, handler, context);
+ FilePaginator.setPageSize(PAGINATOR_PAGE_SIZE);
+ }
+
+ @BeforeClass
+ public static void startUp() throws Exception {
+ HDFSTest.startUp(); // super
+ }
+
+ @AfterClass
+ public static void shutDown() throws Exception {
+ HDFSTest.shutDown(); // super
+ HdfsApi.dropAllConnections(); //cleanup API connection
+ }
+
+ @Test
+ public void testCreateFile() throws IOException, InterruptedException {
+ Response response = createFile("/tmp/testCreateFile", "testCreateFile content");
+
+ ServiceTestUtils.assertHTTPResponseNoContent(response);
+ assertHDFSFileContains("/tmp/testCreateFile", "testCreateFile content");
+ }
+
+ @Test
+ public void testCreateExistingFileForbidden() throws IOException, InterruptedException {
+ createFile("/tmp/testOverwriteFile", "original content");
+ thrown.expect(ServiceFormattedException.class);
+ createFile("/tmp/testOverwriteFile", "new content");
+ }
+
+ @Test
+ public void testCreateFilePathNotExists() throws IOException, InterruptedException {
+ Response response = createFile("/non/existent/path/Luke", null);
+ ServiceTestUtils.assertHTTPResponseNoContent(response);
+
+ Response response2 = createFile("/tmp/Leia", null);
+ ServiceTestUtils.assertHTTPResponseNoContent(response2);
+
+ thrown.expect(ServiceFormattedException.class);
+ Response response3 = createFile("/tmp/Leia", null); // file already exists
+ Assert.assertEquals(400, response3.getStatus());
+ }
+
+ @Test
+ public void testUpdateFileContent() throws Exception {
+ createFile("/tmp/testUpdateFileContent", "some content");
+
+ FileService.FileResourceRequest updateRequest = new FileService.FileResourceRequest();
+ updateRequest.file = new FileResource();
+ updateRequest.file.setFileContent("new content");
+
+ Response response = fileService.updateFile(updateRequest, "/tmp/testUpdateFileContent");
+
+ ServiceTestUtils.assertHTTPResponseNoContent(response);
+ assertHDFSFileContains("/tmp/testUpdateFileContent", "new content");
+ }
+
+ @Test
+ public void testPagination() throws Exception {
+ createFile("/tmp/testPagination", "1234567890"); // 10 bytes, 3 pages if 1 page is 4 bytes
+
+ Response response = fileService.getFilePage("/tmp/testPagination", 0L);
+ ServiceTestUtils.assertHTTPResponseOK(response);
+
+ JSONObject obj = ((JSONObject) response.getEntity());
+ assertFileJsonResponseSanity(obj);
+
+ FileResource firstPage = (FileResource) obj.get("file");
+ Assert.assertEquals("1234", firstPage.getFileContent());
+ Assert.assertEquals(3, firstPage.getPageCount());
+ Assert.assertEquals(0, firstPage.getPage());
+ Assert.assertTrue(firstPage.isHasNext());
+ Assert.assertEquals("/tmp/testPagination", firstPage.getFilePath());
+
+
+ response = fileService.getFilePage("/tmp/testPagination", 1L);
+ ServiceTestUtils.assertHTTPResponseOK(response);
+
+ FileResource secondPage = (FileResource) ((JSONObject) response.getEntity()).get("file");
+ Assert.assertEquals("5678", secondPage.getFileContent());
+ Assert.assertEquals(1, secondPage.getPage());
+ Assert.assertTrue(secondPage.isHasNext());
+
+
+ response = fileService.getFilePage("/tmp/testPagination", 2L);
+ ServiceTestUtils.assertHTTPResponseOK(response);
+
+ FileResource thirdPage = (FileResource) ((JSONObject) response.getEntity()).get("file");
+ Assert.assertEquals("90", thirdPage.getFileContent());
+ Assert.assertEquals(2, thirdPage.getPage());
+ Assert.assertFalse(thirdPage.isHasNext());
+
+
+ thrown.expect(BadRequestFormattedException.class);
+ fileService.getFilePage("/tmp/testPagination", 3L);
+ }
+
+ @Test
+ public void testZeroLengthFile() throws Exception {
+ createFile("/tmp/testZeroLengthFile", "");
+
+ Response response = fileService.getFilePage("/tmp/testZeroLengthFile", 0L);
+
+ ServiceTestUtils.assertHTTPResponseOK(response);
+ JSONObject obj = ((JSONObject) response.getEntity());
+ assertFileJsonResponseSanity(obj);
+
+ FileResource fileResource = (FileResource) obj.get("file");
+ Assert.assertEquals("", fileResource.getFileContent());
+ Assert.assertEquals(0, fileResource.getPage());
+ Assert.assertFalse(fileResource.isHasNext());
+ }
+
+ @Test
+ public void testFileNotFound() throws IOException, InterruptedException {
+ assertHDFSFileNotExists("/tmp/notExistentFile");
+
+ thrown.expect(NotFoundFormattedException.class);
+ fileService.getFilePage("/tmp/notExistentFile", 2L);
+ }
+
+ @Test
+ public void testDeleteFile() throws IOException, InterruptedException {
+ createFile("/tmp/testDeleteFile", "some content");
+
+ assertHDFSFileExists("/tmp/testDeleteFile");
+
+ Response response = fileService.deleteFile("/tmp/testDeleteFile");
+ ServiceTestUtils.assertHTTPResponseNoContent(response);
+
+ assertHDFSFileNotExists("/tmp/testDeleteFile");
+ }
+
+
+
+ private Response createFile(String filePath, String content) throws IOException, InterruptedException {
+ FileService.FileResourceRequest request = new FileService.FileResourceRequest();
+ request.file = new FileResource();
+ request.file.setFilePath(filePath);
+ request.file.setFileContent(content);
+
+ return fileService.createFile(request,
+ ServiceTestUtils.getResponseWithLocation(), ServiceTestUtils.getDefaultUriInfo());
+ }
+
+
+ private void assertFileJsonResponseSanity(JSONObject obj) {
+ Assert.assertTrue(obj.containsKey("file"));
+ }
+
+ private void assertHDFSFileContains(String filePath, String expectedContent) throws IOException {
+ FSDataInputStream fileInputStream = hdfsCluster.getFileSystem().open(new Path(filePath));
+ byte[] buffer = new byte[256];
+ int read = fileInputStream.read(buffer);
+
+ byte[] readData = Arrays.copyOfRange(buffer, 0, read);
+ String actualContent = new String(readData, Charset.forName("UTF-8"));
+
+ Assert.assertEquals(expectedContent, actualContent);
+ }
+
+ private void assertHDFSFileExists(String filePath) throws IOException {
+ Assert.assertTrue( hdfsCluster.getFileSystem().exists(new Path(filePath)) );
+ }
+
+ private void assertHDFSFileNotExists(String filePath) throws IOException {
+ Assert.assertFalse(hdfsCluster.getFileSystem().exists(new Path(filePath)) );
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/JobServiceTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/JobServiceTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/JobServiceTest.java
new file mode 100644
index 0000000..78b6f1f
--- /dev/null
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/JobServiceTest.java
@@ -0,0 +1,210 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.apache.ambari.view.hive.ServiceTestUtils;
+import org.apache.ambari.view.hive.BaseHiveTest;
+import org.apache.ambari.view.hive.utils.HdfsApiMock;
+import org.apache.ambari.view.hive.client.Connection;
+import org.apache.ambari.view.hive.client.ConnectionPool;
+import org.apache.ambari.view.hive.client.HiveClientException;
+import org.apache.ambari.view.hive.resources.savedQueries.SavedQuery;
+import org.apache.ambari.view.hive.resources.savedQueries.SavedQueryService;
+import org.apache.ambari.view.hive.utils.BadRequestFormattedException;
+import org.apache.ambari.view.hive.utils.HdfsApi;
+import org.apache.hive.service.cli.thrift.*;
+import org.json.simple.JSONObject;
+import org.junit.*;
+import org.junit.rules.ExpectedException;
+
+import javax.ws.rs.core.Response;
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import static org.easymock.EasyMock.*;
+
+public class JobServiceTest extends BaseHiveTest {
+ private SavedQueryService savedQueryService;
+ private JobService jobService;
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ @BeforeClass
+ public static void startUp() throws Exception {
+ BaseHiveTest.startUp(); // super
+ }
+
+ @AfterClass
+ public static void shutDown() throws Exception {
+ BaseHiveTest.shutDown(); // super
+ HdfsApi.dropAllConnections(); //cleanup API connection
+ }
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ savedQueryService = getService(SavedQueryService.class, handler, context);
+ jobService = getService(JobService.class, handler, context);
+
+ Connection hiveConnection = configureHiveConnectionMock();
+
+ ConnectionPool.setInstance(context, hiveConnection);
+ }
+
+ @Test
+ public void createJobFromQuery() throws IOException, InterruptedException {
+ setupHdfsApiMock();
+
+ SavedQuery savedQueryForJob = createSavedQuery("Test", null);
+ JobService.JobRequest jobCreationRequest = new JobService.JobRequest();
+ jobCreationRequest.job = new JobImpl();
+ jobCreationRequest.job.setQueryId(savedQueryForJob.getId());
+
+
+ Response response = jobService.create(jobCreationRequest,
+ ServiceTestUtils.getResponseWithLocation(), ServiceTestUtils.getDefaultUriInfo());
+ ServiceTestUtils.assertHTTPResponseCreated(response);
+ JSONObject jobObj = (JSONObject)response.getEntity();
+
+
+ assertResponseJobSanity(jobObj);
+ Assert.assertEquals(getFieldFromJobJSON(jobObj, "queryId"), savedQueryForJob.getId());
+ }
+
+ @Test
+ public void createJobForcedContent() throws IOException, InterruptedException {
+ HdfsApiMock hdfsApiMock = setupHdfsApiMock();
+
+ JobService.JobRequest request = new JobService.JobRequest();
+ request.job = new JobImpl();
+ request.job.setForcedContent("Hello world");
+
+
+ Response response = jobService.create(request,
+ ServiceTestUtils.getResponseWithLocation(), ServiceTestUtils.getDefaultUriInfo());
+ ServiceTestUtils.assertHTTPResponseCreated(response);
+ JSONObject jobObj = (JSONObject)response.getEntity();
+
+
+ assertResponseJobSanity(jobObj);
+ Assert.assertNull(getFieldFromJobJSON(jobObj, "queryId"));
+ Assert.assertEquals("", getFieldFromJobJSON(jobObj, "forcedContent"));
+ Assert.assertEquals("Hello world", hdfsApiMock.getQueryOutputStream().toString());
+ }
+
+ @Test
+ public void createJobNoSource() throws IOException, InterruptedException {
+ HdfsApi hdfsApi = createNiceMock(HdfsApi.class);
+ expect(hdfsApi.mkdir(anyString())).andReturn(true).anyTimes();
+ HdfsApi.setInstance(context, hdfsApi);
+ replay(hdfsApi);
+
+ JobService.JobRequest request = new JobService.JobRequest();
+ request.job = new JobImpl();
+ request.job.setForcedContent(null);
+ request.job.setQueryId(null);
+
+ thrown.expect(BadRequestFormattedException.class);
+ jobService.create(request,
+ ServiceTestUtils.getResponseWithLocation(), ServiceTestUtils.getDefaultUriInfo());
+ }
+
+
+
+ private Connection configureHiveConnectionMock() throws HiveClientException {
+ TGetOperationStatusResp statusResp = getOperationStatusResp();
+ TOperationHandle operationHandle = getExecutionOperationHandle();
+
+ Connection connection = createNiceMock(Connection.class);
+ expect(connection.executeAsync(anyString())).andReturn(operationHandle).anyTimes();
+ expect(connection.getLogs(anyObject(TOperationHandle.class))).andReturn("some logs").anyTimes();
+ expect(connection.getOperationStatus(anyObject(TOperationHandle.class))).andReturn(statusResp).anyTimes();
+
+ replay(connection);
+ return connection;
+ }
+
+ private TGetOperationStatusResp getOperationStatusResp() {
+ TStatus status = new TStatus();
+ status.setStatusCode(TStatusCode.SUCCESS_STATUS);
+
+ TGetOperationStatusResp statusResp = new TGetOperationStatusResp();
+ statusResp.setStatus(status);
+
+ return statusResp;
+ }
+
+ private TOperationHandle getExecutionOperationHandle() {
+ THandleIdentifier handleIdentifier = new THandleIdentifier();
+ handleIdentifier.setGuid("some guid".getBytes());
+ handleIdentifier.setSecret("some secret".getBytes());
+
+ TOperationHandle operationHandle = new TOperationHandle();
+ operationHandle.setHasResultSet(true);
+ operationHandle.setModifiedRowCount(0);
+ operationHandle.setOperationType(TOperationType.EXECUTE_STATEMENT);
+ operationHandle.setOperationId(handleIdentifier);
+ return operationHandle;
+ }
+
+ @Override
+ protected void setupProperties(Map<String, String> properties, File baseDir) throws Exception {
+ super.setupProperties(properties, baseDir);
+ properties.put("scripts.dir", "/tmp/.hiveQueries");
+ properties.put("jobs.dir", "/tmp/.hiveJobs");
+ }
+
+ public static Response doCreateSavedQuery(String title, String path, SavedQueryService service) {
+ SavedQueryService.SavedQueryRequest request = new SavedQueryService.SavedQueryRequest();
+ request.savedQuery = new SavedQuery();
+ request.savedQuery.setTitle(title);
+ request.savedQuery.setQueryFile(path);
+
+ return service.create(request,
+ ServiceTestUtils.getResponseWithLocation(), ServiceTestUtils.getDefaultUriInfo());
+ }
+
+ private SavedQuery createSavedQuery(String title, String path) {
+ Response response = doCreateSavedQuery(title, path, savedQueryService);
+ JSONObject obj = (JSONObject)response.getEntity();
+ SavedQuery query = ((SavedQuery) obj.get("savedQuery"));
+ return query;
+ }
+
+
+ private Object getFieldFromJobJSON(JSONObject jobObj, String field) {
+ return ((Map) jobObj.get("job")).get(field);
+ }
+
+ private HdfsApiMock setupHdfsApiMock() throws IOException, InterruptedException {
+ HdfsApiMock hdfsApiMock = new HdfsApiMock("select * from Z");
+ HdfsApi hdfsApi = hdfsApiMock.getHdfsApi();
+ HdfsApi.setInstance(context, hdfsApi);
+ replay(hdfsApi);
+ return hdfsApiMock;
+ }
+
+ private void assertResponseJobSanity(JSONObject jobObj) {
+ Assert.assertTrue(jobObj.containsKey("job"));
+ Assert.assertNotNull(((Map) jobObj.get("job")).get("id"));
+ Assert.assertNotNull(((Map) jobObj.get("job")).get("queryFile"));
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/LogParserTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/LogParserTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/LogParserTest.java
new file mode 100644
index 0000000..0c060ed
--- /dev/null
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/jobs/LogParserTest.java
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.jobs;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class LogParserTest {
+ @Test
+ public void testParseMRLog() {
+ String log = "INFO : Number of reduce tasks determined at compile time: 1\n" +
+ "INFO : In order to change the average load for a reducer (in bytes):\n" +
+ "INFO : set hive.exec.reducers.bytes.per.reducer=<number>\n" +
+ "INFO : In order to limit the maximum number of reducers:\n" +
+ "INFO : set hive.exec.reducers.max=<number>\n" +
+ "INFO : In order to set a constant number of reducers:\n" +
+ "INFO : set mapreduce.job.reduces=<number>\n" +
+ "WARN : Hadoop command-line option parsing not performed. Implement the Tool interface and execute your application with ToolRunner to remedy this.\n" +
+ "INFO : number of splits:1\n" +
+ "INFO : Submitting tokens for job: job_1421248330903_0003\n" +
+ "INFO : The url to track the job: http://dataworker.hortonworks.com:8088/proxy/application_1421248330903_0003/\n" +
+ "INFO : Starting Job = job_1421248330903_0003, Tracking URL = http://dataworker.hortonworks.com:8088/proxy/application_1421248330903_0003/\n" +
+ "INFO : Kill Command = /usr/hdp/current/hadoop-client/bin/hadoop job -kill job_1421248330903_0003\n" +
+ "INFO : Hadoop job information for Stage-1: number of mappers: 1; number of reducers: 1\n" +
+ "INFO : 2015-01-21 15:03:55,979 Stage-1 map = 0%, reduce = 0%\n" +
+ "INFO : 2015-01-21 15:04:07,503 Stage-1 map = 100%, reduce = 0%, Cumulative CPU 0.79 sec\n" +
+ "INFO : 2015-01-21 15:04:17,384 Stage-1 map = 100%, reduce = 100%, Cumulative CPU 1.86 sec\n" +
+ "INFO : MapReduce Total cumulative CPU time: 1 seconds 860 msec\n" +
+ "INFO : Ended Job = job_1421248330903_0003";
+
+ LogParser p = LogParser.parseLog(log);
+ Assert.assertEquals(1, p.getJobsList().size());
+ Assert.assertEquals("application_1421248330903_0003",(((LogParser.JobId) (p.getJobsList().toArray())[0])
+ .getIdentifier()));
+ }
+
+ @Test
+ public void testParseTezLog() {
+ String log = "INFO : Tez session hasn't been created yet. Opening session\n" +
+ "INFO :\n" +
+ "\n" +
+ "INFO : Status: Running (Executing on YARN cluster with App id application_1423156117563_0003)\n" +
+ "\n" +
+ "INFO : Map 1: -/- Reducer 2: 0/1\n" +
+ "INFO : Map 1: 0/1 Reducer 2: 0/1\n" +
+ "INFO : Map 1: 0/1 Reducer 2: 0/1\n" +
+ "INFO : Map 1: 0(+1)/1 Reducer 2: 0/1\n" +
+ "INFO : Map 1: 0(+1)/1 Reducer 2: 0/1\n" +
+ "INFO : Map 1: 1/1 Reducer 2: 0(+1)/1\n" +
+ "INFO : Map 1: 1/1 Reducer 2: 1/1 ";
+
+ LogParser p = LogParser.parseLog(log);
+ Assert.assertEquals(1, p.getJobsList().size());
+ Assert.assertEquals("application_1423156117563_0003",(((LogParser.JobId) (p.getJobsList().toArray())[0])
+ .getIdentifier()));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/resources/FileResourceServiceTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/resources/FileResourceServiceTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/resources/FileResourceServiceTest.java
new file mode 100644
index 0000000..06d5269
--- /dev/null
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/resources/FileResourceServiceTest.java
@@ -0,0 +1,119 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.resources;
+
+import org.apache.ambari.view.hive.BaseHiveTest;
+import org.apache.ambari.view.hive.resources.resources.FileResourceItem;
+import org.apache.ambari.view.hive.resources.resources.FileResourceService;
+import org.apache.ambari.view.hive.utils.NotFoundFormattedException;
+import org.json.simple.JSONObject;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.net.URI;
+
+import static org.easymock.EasyMock.*;
+
+public class FileResourceServiceTest extends BaseHiveTest {
+ @Rule public ExpectedException thrown = ExpectedException.none();
+ private FileResourceService resourceService;
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ resourceService = getService(FileResourceService.class, handler, context);
+ }
+
+ private Response doCreateFileResourceItem() {
+ FileResourceService.ResourceRequest request = new FileResourceService.ResourceRequest();
+ request.fileResource = new FileResourceItem();
+ request.fileResource.setPath("/tmp/file.jar");
+ request.fileResource.setName("TestFileResourceItem");
+
+ UriInfo uriInfo = createNiceMock(UriInfo.class);
+ URI uri = UriBuilder.fromUri("http://host/a/b").build();
+ expect(uriInfo.getAbsolutePath()).andReturn(uri);
+
+ HttpServletResponse resp_obj = createNiceMock(HttpServletResponse.class);
+
+ resp_obj.setHeader(eq("Location"), anyString());
+
+ replay(uriInfo, resp_obj);
+ return resourceService.create(request, resp_obj, uriInfo);
+ }
+
+ @Test
+ public void createFileResourceItem() {
+ Response response = doCreateFileResourceItem();
+ Assert.assertEquals(201, response.getStatus());
+
+ JSONObject obj = (JSONObject)response.getEntity();
+ Assert.assertTrue(obj.containsKey("fileResource"));
+ Assert.assertNotNull(((FileResourceItem) obj.get("fileResource")).getId());
+ Assert.assertFalse(((FileResourceItem) obj.get("fileResource")).getId() == null);
+ }
+
+ @Test
+ public void resourceNotFound() {
+ thrown.expect(NotFoundFormattedException.class);
+ resourceService.getOne("4242");
+ }
+
+ @Test
+ public void updateFileResourceItem() {
+ Response createdFileResourceItem = doCreateFileResourceItem();
+ Integer createdUdfId = ((FileResourceItem) ((JSONObject) createdFileResourceItem.getEntity()).get("fileResource")).getId();
+
+ FileResourceService.ResourceRequest request = new FileResourceService.ResourceRequest();
+ request.fileResource = new FileResourceItem();
+ request.fileResource.setPath("/tmp/updatedFileResourceItem.jar");
+ request.fileResource.setName("TestFileResourceItem2");
+
+ Response response = resourceService.update(request, String.valueOf(createdUdfId));
+ Assert.assertEquals(204, response.getStatus());
+
+ Response response2 = resourceService.getOne(String.valueOf(createdUdfId));
+ Assert.assertEquals(200, response2.getStatus());
+
+ JSONObject obj = ((JSONObject) response2.getEntity());
+ Assert.assertTrue(obj.containsKey("fileResource"));
+ Assert.assertEquals(((FileResourceItem) obj.get("fileResource")).getName(), request.fileResource.getName());
+ Assert.assertEquals(((FileResourceItem) obj.get("fileResource")).getPath(), request.fileResource.getPath());
+ }
+
+ @Test
+ public void deleteFileResourceItem() {
+ Response createdFileResourceItem = doCreateFileResourceItem();
+ Integer createdUdfId = ((FileResourceItem) ((JSONObject) createdFileResourceItem.getEntity()).get("fileResource")).getId();
+
+ Response response = resourceService.delete(String.valueOf(createdUdfId));
+ Assert.assertEquals(204, response.getStatus());
+
+ thrown.expect(NotFoundFormattedException.class);
+ resourceService.getOne(String.valueOf(createdUdfId));
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryServiceTest.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryServiceTest.java b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryServiceTest.java
new file mode 100644
index 0000000..d369bb2
--- /dev/null
+++ b/contrib/views/hive/src/test/java/org/apache/ambari/view/hive/resources/savedQueries/SavedQueryServiceTest.java
@@ -0,0 +1,177 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.resources.savedQueries;
+
+import org.apache.ambari.view.hive.HDFSTest;
+import org.apache.ambari.view.hive.utils.HdfsApi;
+import org.apache.ambari.view.hive.utils.NotFoundFormattedException;
+import org.json.simple.JSONObject;
+import org.junit.*;
+import org.junit.rules.ExpectedException;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.io.File;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+
+import static org.easymock.EasyMock.*;
+
+public class SavedQueryServiceTest extends HDFSTest {
+ //TODO: run without HDFS cluster
+ private SavedQueryService savedQueryService;
+ @Rule public ExpectedException thrown = ExpectedException.none();
+
+ @BeforeClass
+ public static void startUp() throws Exception {
+ HDFSTest.startUp(); // super
+ }
+
+ @AfterClass
+ public static void shutDown() throws Exception {
+ HDFSTest.shutDown(); // super
+ HdfsApi.dropAllConnections(); //cleanup API connection
+ }
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ savedQueryService = getService(SavedQueryService.class, handler, context);
+ SavedQueryResourceManager.getViewSingletonObjects().clear();
+ }
+
+ @Override
+ protected void setupProperties(Map<String, String> properties, File baseDir) throws Exception {
+ super.setupProperties(properties, baseDir);
+ properties.put("scripts.dir", "/tmp/.hiveQueries");
+ }
+
+ private Response doCreateSavedQuery() {
+ return doCreateSavedQuery("Luke", "/tmp/luke.hql", savedQueryService);
+ }
+
+ public static Response doCreateSavedQuery(String title, String path, SavedQueryService service) {
+ SavedQueryService.SavedQueryRequest request = new SavedQueryService.SavedQueryRequest();
+ request.savedQuery = new SavedQuery();
+ request.savedQuery.setTitle(title);
+ request.savedQuery.setQueryFile(path);
+
+ UriInfo uriInfo = createNiceMock(UriInfo.class);
+ URI uri = UriBuilder.fromUri("http://host/a/b").build();
+ expect(uriInfo.getAbsolutePath()).andReturn(uri);
+
+ HttpServletResponse resp_obj = createNiceMock(HttpServletResponse.class);
+
+ resp_obj.setHeader(eq("Location"), anyString());
+
+ replay(uriInfo, resp_obj);
+ return service.create(request, resp_obj, uriInfo);
+ }
+
+ private Response doCreateSavedQuery(String title, String path) {
+ return doCreateSavedQuery(title, path, savedQueryService);
+ }
+
+ @Test
+ public void createSavedQuery() {
+ Response response = doCreateSavedQuery();
+ Assert.assertEquals(201, response.getStatus());
+
+ JSONObject obj = (JSONObject)response.getEntity();
+ Assert.assertTrue(obj.containsKey("savedQuery"));
+ Assert.assertNotNull(((SavedQuery) obj.get("savedQuery")).getId());
+ Assert.assertTrue(((SavedQuery) obj.get("savedQuery")).getId() != null);
+ }
+
+ @Test
+ public void createSavedQueryAutoCreate() {
+ Response response = doCreateSavedQuery("Test", null);
+ Assert.assertEquals(201, response.getStatus());
+
+ JSONObject obj = (JSONObject)response.getEntity();
+ Assert.assertTrue(obj.containsKey("savedQuery"));
+ Assert.assertNotNull(((SavedQuery) obj.get("savedQuery")).getId());
+ Assert.assertFalse(((SavedQuery) obj.get("savedQuery")).getId() == null);
+ Assert.assertFalse(((SavedQuery) obj.get("savedQuery")).getQueryFile().isEmpty());
+ }
+
+ @Test
+ public void notFound() {
+ thrown.expect(NotFoundFormattedException.class);
+ savedQueryService.getOne("4242");
+ }
+
+ @Test
+ public void update() {
+ Response created = doCreateSavedQuery();
+ Integer createdId = ((SavedQuery) ((JSONObject) created.getEntity()).get("savedQuery")).getId();
+
+ SavedQueryService.SavedQueryRequest request = new SavedQueryService.SavedQueryRequest();
+ request.savedQuery = new SavedQuery();
+ request.savedQuery.setTitle("Updated Query");
+
+ Response response = savedQueryService.update(request, String.valueOf(createdId));
+ Assert.assertEquals(204, response.getStatus());
+
+ Response response2 = savedQueryService.getOne(String.valueOf(createdId));
+ Assert.assertEquals(200, response2.getStatus());
+
+ JSONObject obj = ((JSONObject) response2.getEntity());
+ Assert.assertTrue(obj.containsKey("savedQuery"));
+ Assert.assertEquals(((SavedQuery) obj.get("savedQuery")).getTitle(), request.savedQuery.getTitle());
+ }
+
+ @Test
+ public void delete() {
+ Response created = doCreateSavedQuery();
+ Integer createdId = ((SavedQuery) ((JSONObject) created.getEntity()).get("savedQuery")).getId();
+
+ Response response = savedQueryService.delete(String.valueOf(createdId));
+ Assert.assertEquals(204, response.getStatus());
+
+ thrown.expect(NotFoundFormattedException.class);
+ savedQueryService.getOne(String.valueOf(createdId));
+ }
+
+ @Test
+ public void list() {
+ doCreateSavedQuery("Title 1", "/path/to/file.hql");
+ doCreateSavedQuery("Title 2", "/path/to/file.hql");
+
+ Response response = savedQueryService.getList();
+ Assert.assertEquals(200, response.getStatus());
+
+ JSONObject obj = (JSONObject) response.getEntity();
+ Assert.assertTrue(obj.containsKey("savedQueries"));
+ List<SavedQuery> items = (List<SavedQuery>) obj.get("savedQueries");
+ boolean containsTitle = false;
+ for(SavedQuery item : items)
+ containsTitle = containsTitle || item.getTitle().compareTo("Title 1") == 0;
+ Assert.assertTrue(containsTitle);
+
+ containsTitle = false;
+ for(SavedQuery item : items)
+ containsTitle = containsTitle || item.getTitle().compareTo("Title 2") == 0;
+ Assert.assertTrue(containsTitle);
+ }
+}
[04/12] ambari git commit: AMBARI-9676. Initial Hive View Submission
to Contrib (alexantonenko)
Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/column-filter-widget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/column-filter-widget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/column-filter-widget-test.js
new file mode 100644
index 0000000..315dbbc
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/column-filter-widget-test.js
@@ -0,0 +1,138 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('column-filter-widget', 'ColumnFilterWidgetComponent', {
+ needs: ['component:extended-input']
+});
+
+test('if a filterValue is set when the element is inserted, an action is being sent announcing a filter change', function () {
+ expect(1);
+
+ var column = Ember.Object.create({
+ caption: 'missing.translation'
+ });
+
+ var component = this.subject({ column: column });
+
+ Ember.run(function () {
+ component.set('filterValue', 'initial filter value');
+ });
+
+ var targetObject = {
+ externalAction: function(){
+ ok(true, 'initial filterValue set. Action has been sent.');
+ }
+ };
+
+ component.set('columnFiltered', 'externalAction');
+ component.set('targetObject', targetObject);
+
+ var $component = this.append();
+});
+
+test('isSorted returns true if the table is sorted by this column property', function () {
+ expect(1);
+
+ var component = this.subject();
+
+ var column = Ember.Object.create({
+ property: 'some prop'
+ });
+
+ Ember.run(function () {
+ component.set('column', column);
+ component.set('sortProperties', [column.property]);
+ });
+
+ ok(component.get('isSorted'));
+});
+
+test('isSorted returns false if the table is sorted by some other column', function () {
+ expect(1);
+
+ var component = this.subject();
+
+ var column = Ember.Object.create({
+ property: 'some prop'
+ });
+
+ Ember.run(function () {
+ component.set('column', column);
+ component.set('sortProperties', ['other prop']);
+ });
+
+ ok(!component.get('isSorted'));
+});
+
+test('isSorted returns false if the table is not sorted by any column', function () {
+ expect(1);
+
+ var component = this.subject();
+
+ var column = Ember.Object.create({
+ property: 'some prop'
+ });
+
+ Ember.run(function () {
+ component.set('column', column);
+ component.set('sortProperties', []);
+ });
+
+ ok(!component.get('isSorted'));
+});
+
+test('when sendSort gets called, the columnSorted action gets sent.', function () {
+ expect(1);
+
+ var component = this.subject();
+
+ var targetObject = {
+ externalAction: function(){
+ ok(true, 'columnSorted action has been intercepted.');
+ }
+ };
+
+ Ember.run(function () {
+ component.set('targetObject', targetObject);
+ component.set('columnSorted', 'externalAction');
+
+ component.send('sendSort');
+ });
+});
+
+test('when sendFilter gets called, the columnFiltered action gets sent.', function () {
+ expect(1);
+
+ var component = this.subject();
+
+ var targetObject = {
+ externalAction: function(){
+ ok(true, 'columnFiltered action has been intercepted.');
+ }
+ };
+
+ Ember.run(function () {
+ component.set('targetObject', targetObject);
+ component.set('columnFiltered', 'externalAction');
+
+ component.send('sendFilter');
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/date-range-widget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/date-range-widget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/date-range-widget-test.js
new file mode 100644
index 0000000..e8004cc
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/date-range-widget-test.js
@@ -0,0 +1,130 @@
+/**
+ * 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.
+ */
+
+/* global moment */
+
+import Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('date-range-widget', 'DateRangeWidgetComponent', {
+ needs: ['component:extended-input']
+});
+
+test('Date fields are set correctly', function() {
+ expect(2);
+
+ var component = this.subject();
+
+ var min = moment('04/11/2014', 'DD/MM/YYYY');
+ var max = moment('04/12/2014', 'DD/MM/YYYY');
+ var from = moment('04/11/2014', 'DD/MM/YYYY');
+ var to = moment('04/12/2014', 'DD/MM/YYYY');
+
+ var dateRange = Ember.Object.create({
+ from: from.toString(),
+ to: to.toString(),
+ min: min.toString(),
+ max: max.toString()
+ });
+
+ Ember.run(function() {
+ component.set('dateRange', dateRange);
+ });
+
+ var $component = this.append();
+
+ equal($component.find('.fromDate').val(), moment(from).format('MM/DD/YYYY'), "From date is set correctly");
+ equal($component.find('.toDate').val(), moment(to).format('MM/DD/YYYY'), "To date is set correctly");
+});
+
+test('Date fields updates when the date is changed', function() {
+ expect(2);
+
+ var component = this.subject();
+
+ var min = moment('04/11/2014', 'DD/MM/YYYY');
+ var max = moment('04/12/2014', 'DD/MM/YYYY');
+ var from = moment('04/11/2014', 'DD/MM/YYYY');
+ var to = moment('04/12/2014', 'DD/MM/YYYY');
+
+ var dateRange = Ember.Object.create({
+ from: from.toString(),
+ to: to.toString(),
+ min: min.toString(),
+ max: max.toString()
+ });
+
+ Ember.run(function() {
+ component.set('dateRange', dateRange);
+ });
+
+ var $component = this.append();
+ $component.find('.fromDate').datepicker('setDate', '10/10/2014');
+ $component.find('.toDate').datepicker('setDate', '11/11/2014');
+
+ equal($component.find('.fromDate').val(), '10/10/2014', "From date field is updated");
+ equal($component.find('.toDate').val(), '11/11/2014', "To date field is updated");
+});
+
+test('Display dates are formatted correctly', function(){
+ expect(2);
+
+ var component = this.subject();
+
+ var min = moment('04/11/2014', 'DD/MM/YYYY');
+ var max = moment('04/12/2014', 'DD/MM/YYYY');
+ var from = moment('04/11/2014', 'DD/MM/YYYY');
+ var to = moment('04/12/2014', 'DD/MM/YYYY');
+
+ var dateRange = Ember.Object.create({
+ from: from.toString(),
+ to: to.toString(),
+ min: min.toString(),
+ max: max.toString()
+ });
+
+ Ember.run(function () {
+ component.set('dateRange', dateRange);
+ });
+
+ equal(component.get('displayFromDate'), '11/04/2014', "displayFromDate is formatted correctly");
+ equal(component.get('displayToDate'), '12/04/2014', "displayToDate is formatted correctly");
+});
+
+test('If from/to are not passed they are set to min/max', function() {
+ expect(2);
+
+ var component = this.subject();
+
+ var min = moment('04/11/2014', 'DD/MM/YYYY');
+ var max = moment('04/12/2014', 'DD/MM/YYYY');
+
+ var dateRange = Ember.Object.create({
+ min: min.toString(),
+ max: max.toString()
+ });
+
+ Ember.run(function () {
+ component.set('dateRange', dateRange);
+ });
+
+ var $component = this.append();
+
+ equal(component.get('dateRange.from'), min.toString(), "From date is to min date");
+ equal(component.get('dateRange.to'), max.toString(), "To date is set to max date");
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/expander-widget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/expander-widget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/expander-widget-test.js
new file mode 100644
index 0000000..65a50d1
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/expander-widget-test.js
@@ -0,0 +1,58 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('expander-widget', 'ExpanderWidgetComponent', {
+});
+
+test('should set the heading when provided.', function () {
+ expect(2);
+
+ var component = this.subject();
+ var $component = this.append();
+ var heading = 'some header';
+
+ equal($component.find('.accordion-toggle').text(), '');
+
+ Ember.run(function () {
+ component.set('heading', heading);
+ });
+
+ equal($component.find('.accordion-toggle').text(), heading);
+});
+
+test('should correctly toggle isExpanded property.', function () {
+ expect(2);
+
+ var component = this.subject();
+ this.append();
+
+ Ember.run(function(){
+ component.send('toggle');
+ });
+
+ equal(component.get('isExpanded'), true);
+
+ Ember.run(function(){
+ component.send('toggle');
+ });
+
+ equal(component.get('isExpanded'), false);
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/extended-input-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/extended-input-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/extended-input-test.js
new file mode 100644
index 0000000..3ef5cf9
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/extended-input-test.js
@@ -0,0 +1,79 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('extended-input', 'ExtendedInputComponent');
+
+test('Component has dynamicValue and dynamicContext', function () {
+ expect(1);
+
+ var component = this.subject({
+ dynamicValue: 'dynamicValue',
+ dynamicContext: Ember.Object.create({ 'dynamicValue' : 'test' })
+ });
+
+ var $component = this.append();
+
+ equal(component.get('value'), 'test', 'Value is set to dynamicValue value');
+});
+
+
+test('Component has no dynamicValue and dynamicContext', function () {
+ expect(1);
+
+ var component = this.subject();
+ var $component = this.append();
+
+ ok(!component.get('value'), 'Value is not set as dynamicValue value');
+});
+
+test("Component's dynamicValue is set", function () {
+ expect(1);
+
+ var component = this.subject({
+ dynamicValue: 'dynamicValue',
+ dynamicContext: Ember.Object.create({ 'dynamicValue' : 'test' })
+ });
+
+ var $component = this.append();
+
+ Ember.run(function() {
+ component.sendValueChanged();
+
+ equal(component.get('value'), component.dynamicContext.get('dynamicValue'), "Value is set and dynamicValue is set");
+ });
+});
+
+test("Component's dynamicValue is not set", function () {
+ expect(1);
+
+ var component = this.subject({
+ dynamicValue: 'dynamicValue',
+ dynamicContext: Ember.Object.create({ })
+ });
+
+ var $component = this.append();
+
+ Ember.run(function() {
+ component.sendValueChanged();
+
+ equal(component.get('value'), undefined, "Value is not set");
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/no-bubbling-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/no-bubbling-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/no-bubbling-test.js
new file mode 100644
index 0000000..4ce4ac3
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/no-bubbling-test.js
@@ -0,0 +1,43 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('no-bubbling', 'NoBubblingWidgetComponent', {
+});
+
+
+test('External actions', function() {
+ expect(2);
+
+ var component = this.subject({
+ targetObject: {
+ click: function(data) {
+ ok(true, 'External click action called');
+ equal(data, 'data', 'Data is sent with the action');
+ }
+ },
+ click: 'click',
+ data: 'data'
+ });
+
+ var $component = this.append();
+
+ $component.trigger('click');
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/number-range-widget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/number-range-widget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/number-range-widget-test.js
new file mode 100644
index 0000000..dc95cf7
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/number-range-widget-test.js
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* global moment */
+
+import Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('number-range-widget', 'NumberRangeWidgetComponent', {
+ needs: ['component:extended-input']
+});
+
+
+test('Component is initialized correctly', function() {
+ expect(3);
+
+ var numberRange = Ember.Object.create({
+ max: 1,
+ min: 0
+ });
+
+ var component = this.subject({ numberRange: numberRange });
+ var $component = this.append();
+
+ ok(component.get('slider'), 'Slider is set');
+
+ equal(component.get('numberRange.from'), numberRange.get('min'), 'from is set to min');
+ equal(component.get('numberRange.to'), numberRange.get('max'), 'to is set to max');
+
+});
+
+test('external change action is called', function() {
+ expect(1);
+
+ var targetObject = {
+ rangeChanged: function() {
+ ok(true, 'rangeChanged external action called');
+ }
+ };
+
+ var numberRange = Ember.Object.create({
+ max: 1,
+ min: 0
+ });
+
+ var component = this.subject({
+ numberRange: numberRange,
+ targetObject: targetObject,
+ rangeChanged: 'rangeChanged'
+ });
+
+ var $component = this.append();
+
+ Ember.run(function() {
+ component.get('slider').slider('value', 1);
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/popover-widget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/popover-widget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/popover-widget-test.js
new file mode 100644
index 0000000..dd6ddd9
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/popover-widget-test.js
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('popover-widget', 'PopoverWidgetComponent');
+
+test('Component initializes correctly', function () {
+ expect(2);
+
+ var component = this.subject({
+ template: Ember.Handlebars.compile("test")
+ });
+ var $component = this.append();
+
+ ok($component, "Popover element is initialized");
+ equal($component.attr('data-content').trim(), "test", "data-content is populated");
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/progress-widget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/progress-widget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/progress-widget-test.js
new file mode 100644
index 0000000..169dc7f
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/progress-widget-test.js
@@ -0,0 +1,45 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('progress-widget', 'ProgressWidgetComponent');
+
+test('Setting progress attributes', function () {
+ expect(3);
+
+ var component = this.subject({
+ stages: [
+ Ember.Object.create({ className: 'progress-bar-success', name: 'Execution', value: 10 }),
+ Ember.Object.create({ className: 'progress-bar-danger', name: 'Queued', value: 30 }),
+ Ember.Object.create({ className: 'progress-bar-warning', name: 'Compile', value: 30 })
+ ],
+ formattedStages: Ember.ArrayProxy.create({ content: [] })
+ });
+
+ var $component = this.append();
+
+ Ember.run(function() {
+ component.formatStages();
+
+ equal(component.get('stages').get('firstObject').get('className'), 'progress-bar-success', 'ClassName was set correctly');
+ equal(component.get('stages').get('firstObject').get('name'), 'Execution', 'Name was set correctly');
+ equal(component.get('stages').get('firstObject').get('value'), 10, 'Value was set correctly');
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/query-editor-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/query-editor-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/query-editor-test.js
new file mode 100644
index 0000000..0656ee1
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/query-editor-test.js
@@ -0,0 +1,50 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('query-editor', 'QueryEditorComponent');
+
+test('initEditor sets the editor on didInsertElement', function () {
+ expect(2);
+
+ var component = this.subject();
+
+ equal(component.get('editor'), undefined, 'element not rendered. Editor not set.');
+
+ this.append();
+
+ ok(component.get('editor'), 'element rendered. Editor set.');
+});
+
+test('updateValue sets the query value on the editor.', function () {
+ expect(1);
+
+ var component = this.subject();
+
+ var query = 'select something';
+
+ this.append();
+
+ Ember.run(function () {
+ component.set(('query'), query);
+ });
+
+ equal(component.get('editor').getValue(), query, 'set query property. Updated editor value property.');
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/select-widget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/select-widget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/select-widget-test.js
new file mode 100644
index 0000000..9187d00
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/select-widget-test.js
@@ -0,0 +1,158 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('select-widget', 'SelectWidgetComponent', {
+ needs: ['helper:path-binding']
+});
+
+test('selectedLabel returns the selectedValue property indicated by labelPath if selectedValue and labelPath are set.', function () {
+ expect(1);
+
+ var component = this.subject();
+
+ var selectedValue = Ember.Object.extend({
+ label: 'db'
+ }).create();
+
+ var labelPath = 'label';
+
+ Ember.run(function () {
+ component.set('labelPath', labelPath);
+ component.set('selectedValue', selectedValue);
+ });
+
+ equal(component.get('selectedLabel'), selectedValue.label, 'selectedValue and labelPath are set. selectedLabel returns selectedValue[labelPath].');
+});
+
+test('selectedLabel returns defaultLabel if selectedValue is falsy and defaultLabel is set.', function () {
+ expect(1);
+
+ var component = this.subject();
+
+ var defaultLabel = 'select...';
+
+ Ember.run(function () {
+ component.set('defaultLabel', defaultLabel);
+ });
+
+ equal(component.get('selectedLabel'), defaultLabel, 'selectedValue falsy and defaultLabel set. selectedLabel returns defaultLabel.');
+});
+
+test('selectedLabel returns undefined if neither selectedValue nor defaultLabel are set.', function () {
+ expect(1);
+
+ var component = this.subject();
+
+ equal(component.get('selectedLabel'), undefined, 'selectedValue and defaultLabel are falsy. selectedLabel returns undefined.');
+});
+
+test('selectedLabel is computed when selectedValue changes.', function () {
+ expect(2);
+
+ var component = this.subject();
+
+ var selectedValue = Ember.Object.extend({
+ label: 'db'
+ }).create();
+
+ var labelPath = 'label';
+
+ equal(component.get('selectedLabel'), undefined, 'selectedValue and defaultLabel are falsy. selectedLabel returns undefined.');
+
+ Ember.run(function () {
+ component.set('labelPath', labelPath);
+ component.set('selectedValue', selectedValue);
+ });
+
+ equal(component.get('selectedLabel'), selectedValue.label, 'selectedValue and labelPath are set. selectedLabel returns selectedValue[labelPath].');
+});
+
+test('renders an li tag for each item in the items collection.', function () {
+ expect(2);
+
+ var component = this.subject();
+ var $component = this.append();
+
+ equal($component.find('li').length, 0, 'items collection is not set. No li tags are rendered.');
+
+ Ember.run(function() {
+ var items = Ember.ArrayProxy.create({ content: Ember.A([Ember.Object.create(), Ember.Object.create()])});
+ component.set('labelPath', 'name');
+ component.set('items', items);
+ });
+
+ equal($component.find('li').length, 2, 'items collection is set containing one item. One li tag is rendered.');
+});
+
+test('if no selected item nor defaultLabel set the selected value with first item', function () {
+ expect(1);
+
+ var items = [
+ 'item1',
+ 'item2'
+ ];
+
+ var component = this.subject({ items: items });
+ var $component = this.append();
+
+ equal(component.get('selectedValue'), 'item1', 'selectedValue is set to first item')
+});
+
+test('component actions', function() {
+ expect(7);
+
+ var targetObject = {
+ itemAdded: function() {
+ ok(true, 'External action itemAdded called')
+ },
+ itemEdited: function(item) {
+ ok(true, 'External action itemEdited called');
+ equal(item, 'editedItem', 'Data is sent with action');
+ },
+ itemRemoved: function(item) {
+ ok(true, 'External action itemRemoved called');
+ equal(item, 'removedItem', 'Data is sent with action');
+ }
+ };
+ var component = this.subject({
+ items: ['item'],
+ itemAdded: 'itemAdded',
+ itemEdited: 'itemEdited',
+ itemRemoved: 'itemRemoved',
+ targetObject: targetObject
+ });
+
+ var $component = this.append();
+
+ equal(component.get('selectedValue'), 'item', 'selectedValue is set to first item');
+
+ Ember.run(function() {
+ component.send('select', 'newItem');
+ component.send('add');
+ component.send('edit', 'editedItem');
+ component.send('remove', 'removedItem');
+ });
+
+ equal(component.get('selectedValue'), 'newItem', 'selectedValue is set to newItem');
+
+
+
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/tabs-wiget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/tabs-wiget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/tabs-wiget-test.js
new file mode 100644
index 0000000..f3a1c5f
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/tabs-wiget-test.js
@@ -0,0 +1,117 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('tabs-widget', 'TabsWidgetComponent', {
+ needs: []
+});
+
+test('First tab active by default', function() {
+ expect(2);
+
+ var tabs = Ember.ArrayProxy.create({content: Ember.A([
+ Ember.Object.create(),
+ Ember.Object.create()
+ ])});
+
+ var component = this.subject({ tabs: tabs });
+ var $component = this.append();
+
+ ok(component.get('tabs.firstObject.active'), 'First tab is active');
+ ok(!component.get('tabs.lastObject.active'), 'Second tab is not active');
+});
+
+
+test('Set active tab on init', function() {
+ expect(2);
+
+ var tabs = Ember.ArrayProxy.create({content: Ember.A([
+ Ember.Object.create(),
+ Ember.Object.create(),
+ Ember.Object.create({ active: true })
+ ])});
+
+ var component = this.subject({ tabs: tabs });
+
+ ok(!component.get('tabs.firstObject.active'), 'First tab is not active');
+ ok(component.get('tabs.lastObject.active'), 'Last tab is active');
+});
+
+
+test('Set active tab', function() {
+ expect(3);
+
+ var tabs = Ember.ArrayProxy.create({content: Ember.A([
+ Ember.Object.create(),
+ Ember.Object.create(),
+ Ember.Object.create({ active: true })
+ ])});
+
+ var component = this.subject({ tabs: tabs });
+
+ ok(!component.get('tabs.firstObject.active'), 'First tab is not active');
+ ok(component.get('tabs.lastObject.active'), 'Last tab is active');
+
+ Ember.run(function() {
+ component.send('selectTab', tabs.objectAt(1));
+ });
+
+ ok(component.get('tabs').objectAt(1).get('active'), 'Second tab is active');
+});
+
+test('removeEnabled tabs', function() {
+ expect(2);
+
+ var tabs = Ember.ArrayProxy.create({content: Ember.A([
+ Ember.Object.create(),
+ Ember.Object.create(),
+ Ember.Object.create({ active: true })
+ ])});
+
+ var component = this.subject({ tabs: tabs, canRemove: true });
+
+ ok(component.get('removeEnabled'), 'More than one tab removeEnabled returns true');
+
+ Ember.run(function() {
+ component.get('tabs').popObject();
+ component.get('tabs').popObject();
+ });
+
+ ok(!component.get('removeEnabled'), 'Only one tab removeEnabled returns false');
+});
+
+test('remove tab', function () {
+ expect(1);
+
+ var targetObject = {
+ removeTabAction: function() {
+ ok(true, 'External remove tab action called');
+ }
+ };
+
+ var component = this.subject({
+ 'removeClicked': 'removeTabAction',
+ 'targetObject': targetObject
+ });
+
+ Ember.run(function() {
+ component.send('remove', {});
+ });
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/typeahead-widget-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/typeahead-widget-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/typeahead-widget-test.js
new file mode 100644
index 0000000..3f2312b
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/components/typeahead-widget-test.js
@@ -0,0 +1,45 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleForComponent, test } from 'ember-qunit';
+
+moduleForComponent('typeahead-widget', 'TypeaheadWidgetComponent', {
+ needs: ['component:ember-selectize']
+});
+
+test('Component is initialized correctly', function () {
+ expect(2);
+
+ var items = [
+ {name: 'item 1', id: 1},
+ {name: 'item 2', id: 2},
+ {name: 'item 3', id: 3},
+ {name: 'item 4', id: 4}
+ ];
+
+ var component = this.subject({
+ content: items,
+ optionValuePath: 'content.id',
+ optionLabelPath: 'content.name'
+ });
+ var $component = this.append();
+
+ equal(component.get('content.length'), items.length, 'Items are set');
+ equal(component.get('selection'), items[0], 'First object is set as default value');
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/columns.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/columns.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/columns.js
new file mode 100644
index 0000000..f99a5e4
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/columns.js
@@ -0,0 +1,32 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:columns', 'ColumnsController', {
+ needs: ['adapter:database']
+});
+
+test('controller is initialized properly.', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ ok(controller.get('baseUrl'), 'baseUrl was set to a truthy value.');
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/databases-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/databases-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/databases-test.js
new file mode 100644
index 0000000..6bcc733
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/databases-test.js
@@ -0,0 +1,214 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:databases', 'DatabasesController', {
+ needs: ['component:select-widget',
+ 'component:expander-widget',
+ 'adapter:database',
+ 'controller:tables',
+ 'controller:columns',
+ 'controller:open-queries' ]
+});
+
+test('controller is initialized properly.', function () {
+ expect(6);
+
+ var controller = this.subject();
+
+ ok(controller.get('baseUrl'), 'baseUrl was set to a truthy value.');
+ ok(controller.get('tableSearchResults'), 'table search results collection was initialized.');
+ ok(controller.get('tabs'), 'tabs collection was initialized.');
+ equal(controller.get('tabs.length'), 2, 'tabs collection contains two tabs');
+ equal(controller.get('tabs').objectAt(0).get('name'), Ember.I18n.t('titles.explorer'), 'first tab is database explorer.');
+ equal(controller.get('tabs').objectAt(1).get('name'), Ember.I18n.t('titles.results'), 'second tab is search results');
+});
+
+test('setTablePageAvailability sets canGetNextPage true if given database hasNext flag is true.', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ var database = Ember.Object.create( { hasNext: true } );
+
+ controller.setTablePageAvailability(database);
+
+ equal(database.get('canGetNextPage'), true);
+});
+
+test('setTablePageAvailability sets canGetNextPage true if given database has more loaded tables than the visible ones.', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ var database = Ember.Object.create({
+ tables: [1],
+ visibleTables: []
+ });
+
+ controller.setTablePageAvailability(database);
+
+ equal(database.get('canGetNextPage'), true);
+});
+
+test('setTablePageAvailability sets canGetNextPage falsy if given database hasNext flag is falsy and all loaded tables are visible.', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ var database = Ember.Object.create({
+ tables: [1],
+ visibleTables: [1]
+ });
+
+ controller.setTablePageAvailability(database);
+
+ ok(!database.get('canGetNextPage'));
+});
+
+test('setColumnPageAvailability sets canGetNextPage true if given table hasNext flag is true.', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ var table = Ember.Object.create( { hasNext: true } );
+
+ controller.setColumnPageAvailability(table);
+
+ equal(table.get('canGetNextPage'), true);
+});
+
+test('setColumnPageAvailability sets canGetNextPage true if given table has more loaded columns than the visible ones.', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ var table = Ember.Object.create({
+ columns: [1],
+ visibleColumns: []
+ });
+
+ controller.setColumnPageAvailability(table);
+
+ equal(table.get('canGetNextPage'), true);
+});
+
+test('setColumnPageAvailability sets canGetNextPage true if given database hasNext flag is falsy and all loaded columns are visible.', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ var table = Ember.Object.create({
+ columns: [1],
+ visibleColumns: [1]
+ });
+
+ controller.setColumnPageAvailability(table);
+
+ ok(!table.get('canGetNextPage'));
+});
+
+test('getTables sets the visibleTables as the first page of tables if they are already loaded', function () {
+ expect(2);
+
+ var controller = this.subject();
+
+ var database = Ember.Object.create({
+ name: 'test_db',
+ tables: [1, 2, 3]
+ });
+
+ controller.pushObject(database);
+ controller.set('pageCount', 2);
+
+ controller.send('getTables', 'test_db');
+
+ equal(database.get('visibleTables.length'), controller.get('pageCount'), 'there are 2 visible tables out of 3.');
+ equal(database.get('canGetNextPage'), true, 'user can get next tables page.');
+});
+
+test('getColumns sets the visibleColumns as the first page of columns if they are already loaded.', function () {
+ expect(2);
+
+ var controller = this.subject();
+
+ var table = Ember.Object.create({
+ name: 'test_table',
+ columns: [1, 2, 3]
+ });
+
+ var database = Ember.Object.create({
+ name: 'test_db',
+ tables: [ table ],
+ visibleTables: [ table ]
+ });
+
+ controller.set('pageCount', 2);
+
+ controller.send('getColumns', 'test_table', database);
+
+ equal(table.get('visibleColumns.length'), controller.get('pageCount'), 'there are 2 visible columns out of 3.');
+ equal(table.get('canGetNextPage'), true, 'user can get next columns page.');
+});
+
+test('showMoreTables pushes more tables to visibleTables if there are still hidden tables loaded.', function () {
+ expect(2);
+
+ var controller = this.subject();
+
+ var database = Ember.Object.create({
+ name: 'test_db',
+ tables: [1, 2, 3],
+ visibleTables: [1]
+ });
+
+ controller.pushObject(database);
+ controller.set('pageCount', 1);
+
+ controller.send('showMoreTables', database);
+
+ equal(database.get('visibleTables.length'), controller.get('pageCount') * 2, 'there are 2 visible tables out of 3.');
+ equal(database.get('canGetNextPage'), true, 'user can get next tables page.');
+});
+
+test('showMoreColumns pushes more columns to visibleColumns if there are still hidden columns loaded.', function () {
+ expect(2);
+
+ var controller = this.subject();
+
+ var table = Ember.Object.create({
+ name: 'test_table',
+ columns: [1, 2, 3],
+ visibleColumns: [1]
+ });
+
+ var database = Ember.Object.create({
+ name: 'test_db',
+ tables: [ table ],
+ visibleTables: [ table ]
+ });
+
+ controller.set('pageCount', 1);
+
+ controller.send('showMoreColumns', table, database);
+
+ equal(table.get('visibleColumns.length'), controller.get('pageCount') * 2, 'there are 2 visible columns out of 3.');
+ equal(table.get('canGetNextPage'), true, 'user can get next columns page.');
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/history-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/history-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/history-test.js
new file mode 100644
index 0000000..c43cfda
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/history-test.js
@@ -0,0 +1,117 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:history', 'HistoryController', {
+ needs: ['controller:job', 'controller:loaded-files']
+});
+
+test('controller is initialized correctly', function () {
+ expect(1);
+
+ var component = this.subject();
+
+ equal(component.get('columns.length'), 4, 'Columns are initialized');
+});
+
+test('date range is set correctly', function () {
+ expect(2);
+
+ var component = this.subject();
+ var min = parseInt(Date.now() / 1000) - (60 * 60 * 24 * 60);
+ var max = parseInt(Date.now() / 1000);
+
+ var history = Ember.ArrayProxy.create({ content: [
+ Ember.Object.create({
+ dateSubmitted: min
+ }),
+ Ember.Object.create({
+ dateSubmitted: max
+ })
+ ]});
+
+ Ember.run(function() {
+ component.set('history', history);
+ });
+
+ var dateColumn = component.get('columns').find(function (column) {
+ return column.get('caption') === 'columns.date';
+ });
+
+ equal(dateColumn.get('dateRange.min'), min, 'Min date is set correctly');
+ equal(dateColumn.get('dateRange.max'), max, 'Max date is set correctly');
+});
+
+test('interval duration is set correctly', function () {
+ expect(2);
+
+ var component = this.subject();
+
+ var history = Ember.ArrayProxy.create({ content: [
+ Ember.Object.create({
+ duration: 20
+ }),
+ Ember.Object.create({
+ duration: 300
+ })
+ ]});
+
+ Ember.run(function() {
+ component.set('history', history);
+ });
+
+ var durationColumn = component.get('columns').find(function (column) {
+ return column.get('caption') === 'columns.duration';
+ });
+
+ equal(durationColumn.get('numberRange.min'), 20, 'Min value is set correctly');
+ equal(durationColumn.get('numberRange.max'), 300, 'Max value is set correctly');
+});
+
+test('history filtering', function() {
+ expect(2);
+
+ var component = this.subject();
+
+ var history = Ember.ArrayProxy.create({
+ content: [
+ Ember.Object.create({
+ name: 'HISTORY',
+ status: 1
+ }),
+ Ember.Object.create({
+ name: '1HISTORY',
+ status: 2
+ })
+ ]
+ });
+
+ Ember.run(function() {
+ component.set('history', history);
+ });
+
+ equal(component.get('model.length'), 2, 'No filters applied we have 2 models');
+
+ Ember.run(function() {
+ component.filterBy('name', 'HISTORY', true);
+ });
+
+ equal(component.get('model.length'), 1, 'Filter by name we have 1 filtered model');
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js
new file mode 100644
index 0000000..291cc7e
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/index-test.js
@@ -0,0 +1,179 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:index', 'IndexController', {
+ needs: ['controller:databases', 'controller:open-queries', 'controller:insert-udfs',
+ 'controller:udfs', 'controller:loaded-files',
+ 'controller:index/history-query/logs',
+ 'controller:index/history-query/results',
+ 'controller:index/history-query/explain',
+ 'controller:settings',
+ 'adapter:database', 'controller:tables', 'controller:columns']
+});
+
+test('when initialized, controller sets the queryProcessTabs.', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ ok(controller.get('queryProcessTabs', 'queryProcessTabs is initialized.'));
+});
+
+test('databasesChanged sets null the selectedTables property of open-queries if databases controller has not set its selectedDatabase.tables property', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ equal(controller.get('databases.selectedTables'), null, 'databases controller property selectedDatabase.tables not set. open-queries selectedTables returns null');
+});
+
+test('modelChanged calls update on the open-queries cotnroller.', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ controller.set('openQueries.update', function () {
+ var defer = Ember.RSVP.defer();
+
+ ok(true, 'index model has changed. update was called on open-queries controller.');
+
+ defer.resolve();
+
+ return defer.promise;
+ });
+
+ Ember.run(function () {
+ controller.set('model', Ember.Object.create());
+ });
+});
+
+test('bindQueryParams replaces param placeholder with values', function() {
+ expect(1);
+
+ var controller = this.subject();
+ var queryParams = [
+ { name: '$what', value: 'color' },
+ { name: '$where', value: 'z'}
+ ];
+
+ var query = "select $what from $where";
+ var replacedQuery = "select color from z";
+
+ Ember.run(function() {
+ controller.get('queryParams').setObjects(queryParams);
+ });
+
+ equal(controller.bindQueryParams(query), replacedQuery, 'Params replaced correctly');
+});
+
+test('bindQueryParams replaces same param multiple times', function() {
+ expect(1);
+
+ var controller = this.subject();
+ var queryParams = [
+ { name: '$what', value: 'color' },
+ { name: '$where', value: 'z'}
+ ];
+
+ var query = "select $what from $where as $what";
+ var replacedQuery = "select color from z as color";
+
+ Ember.run(function() {
+ controller.get('queryParams').setObjects(queryParams);
+ });
+
+ equal(controller.bindQueryParams(query), replacedQuery, 'Params replaced correctly');
+});
+
+test('parseQueryParams sets queryParams when query changes', function() {
+ expect(3);
+
+ var controller = this.subject();
+
+ var query = "select $what from $where";
+
+ Ember.run(function() {
+ controller.set('openQueries.currentQuery', {
+ 'fileContent': query
+ });
+ });
+
+ equal(controller.get('queryParams.length'), 2, '2 queryParams parsed');
+ equal(controller.get('queryParams').objectAt(0).name, '$what', 'First param parsed correctly');
+ equal(controller.get('queryParams').objectAt(1).name, '$where', 'Second param parsed correctly');
+});
+
+test('canExecute return false if query is executing', function() {
+ expect(2);
+ var controller = this.subject();
+
+ Ember.run(function() {
+ controller.set('openQueries.update', function () {
+ var defer = Ember.RSVP.defer();
+ defer.resolve();
+ return defer.promise;
+ });
+
+ controller.set('model', Ember.Object.create({ 'isRunning': false }));
+ controller.set('queryParams', []);
+ });
+
+ ok(controller.get('canExecute'), 'Query is not executing => canExecute return true');
+
+ Ember.run(function() {
+ controller.set('model', Ember.Object.create({ 'isRunning': true }));
+ });
+
+ ok(!controller.get('canExecute'), 'Query is executing => canExecute return false');
+});
+
+test('canExecute return false if queryParams doesnt\'t have values', function() {
+ expect(2);
+ var controller = this.subject();
+
+ var paramsWithoutValues = [
+ { name: '$what', value: '' },
+ { name: '$where', value: '' }
+ ];
+
+ var paramsWithValues = [
+ { name: '$what', value: 'value1' },
+ { name: '$where', value: 'value2' }
+ ];
+
+ Ember.run(function() {
+ controller.set('openQueries.update', function () {
+ var defer = Ember.RSVP.defer();
+ defer.resolve();
+ return defer.promise;
+ });
+ controller.set('model', Ember.Object.create({ 'isRunning': false }));
+ controller.get('queryParams').setObjects(paramsWithoutValues);
+ });
+
+ ok(!controller.get('canExecute'), 'Params without value => canExecute return false');
+
+ Ember.run(function() {
+ controller.get('queryParams').setObjects(paramsWithValues);
+ });
+
+ ok(controller.get('canExecute'), 'Params with values => canExecute return true');
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/insert-udfs-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/insert-udfs-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/insert-udfs-test.js
new file mode 100644
index 0000000..84803d4
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/insert-udfs-test.js
@@ -0,0 +1,68 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:insert-udfs', 'InsertUdfsController', {
+ needs: ['controller:udf', 'controller:udfs', 'controller:file-resources']
+});
+
+test('controller is initialized correctly', function () {
+ expect(1);
+
+ var udfs = Ember.A([
+ Ember.Object.create({ fileResource: { id: 1 } }),
+ Ember.Object.create({ fileResource: { id: 1 } }),
+ Ember.Object.create({ fileResource: { id: 2 } }),
+ Ember.Object.create({ fileResource: { id: 2 } })
+ ]);
+
+ var component = this.subject();
+
+ Ember.run(function() {
+ component.set('udfs', udfs);
+ });
+
+ equal(component.get('length'), 2, 'should contain unique file resources');
+});
+
+test('controller updates on new udfs', function () {
+ expect(2);
+
+ var udfs = Ember.A([
+ Ember.Object.create({ fileResource: { id: 1 } }),
+ Ember.Object.create({ fileResource: { id: 2 } }),
+ ]);
+
+ var component = this.subject();
+
+ Ember.run(function() {
+ component.set('udfs', udfs);
+ });
+
+ equal(component.get('length'), 2, '');
+
+ var newUdf = Ember.Object.create({ isNew: true, fileResource: { id: 3 } });
+
+ Ember.run(function() {
+ component.get('udfs').pushObject(newUdf);
+ });
+
+ equal(component.get('length'), 3, '');
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/job-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/job-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/job-test.js
new file mode 100644
index 0000000..1c76352
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/job-test.js
@@ -0,0 +1,62 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:job', 'JobController', {
+ needs: ['controller:history', 'controller:loaded-files']
+});
+
+test('Statuses are computed correctly', function () {
+ expect(5);
+
+ var component = this.subject();
+
+ Ember.run(function() {
+ component.set('content', Ember.Object.create());
+ component.set('content.status', constants.statuses.running);
+ });
+
+ ok(component.get('canStop'), 'Status is running canStop returns true');
+
+ Ember.run(function() {
+ component.set('content.status', constants.statuses.initialized);
+ });
+
+ ok(component.get('canStop'), 'Status is initialized canStop returns true');
+
+ Ember.run(function() {
+ component.set('content.status', constants.statuses.pending);
+ });
+
+ ok(component.get('canStop'), 'Status is pending canStop returns true');
+
+ Ember.run(function() {
+ component.set('content.status', constants.statuses.canceled);
+ });
+
+ ok(!component.get('canStop'), 'Status is canceled canStop returns false');
+
+ Ember.run(function() {
+ component.set('content.status', constants.statuses.unknown);
+ });
+
+ ok(!component.get('canStop'), 'Status is unknown canStop returns false');
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/open-queries-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/open-queries-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/open-queries-test.js
new file mode 100644
index 0000000..b1c581a
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/open-queries-test.js
@@ -0,0 +1,103 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:open-queries', 'OpenQueriesController', {
+ needs: [ 'controller:databases',
+ 'controller:loaded-files',
+ 'controller:index/history-query/results',
+ 'controller:index/history-query/explain',
+ 'controller:index',
+ 'controller:columns',
+ 'controller:settings'
+ ]
+});
+
+test('when initialized, controller sets the queryTabs.', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ ok(controller.get('queryTabs', 'queryTabs is initialized.'));
+});
+
+test('pushObject override creates a new queryFile mock and adds it to the collection if none provided.', function () {
+ expect(3);
+
+ var controller = this.subject();
+
+ var model = Ember.Object.create({
+ id: 5
+ });
+
+ controller.pushObject(null, model);
+
+ equal(controller.get('length'), 1, 'a new object was added to the open queries collection.');
+ equal(controller.objectAt(0).id, model.get('id'), 'the object id was set to the model id.');
+ equal(controller.objectAt(0).get('fileContent'), '', 'the object fileContent is initialized with empty string.');
+});
+
+test('getTabForModel retrieves the tab that has the id and the type equal to the ones of the given model.', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ var model = Ember.Object.create({
+ id: 1
+ });
+
+ controller.get('queryTabs').pushObject(Ember.Object.create({
+ id: model.get('id')
+ }));
+
+ equal(controller.getTabForModel(model), controller.get('queryTabs').objectAt(0), 'retrieves correct tab for the given model.');
+});
+
+test('getQueryForModel retrieves the query by id equality if a new record is given', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ var model = Ember.Object.create({
+ id: 1,
+ isNew: true
+ });
+
+ controller.pushObject(null, model);
+
+ equal(controller.getQueryForModel(model).get('id'), model.get('id'), 'a new record was given, the method retrieves the query by id equality');
+});
+
+test('getQueryForModel retrieves the query by record id equality with model queryFile path if a saved record is given', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ var model = Ember.Object.create({
+ id: 1,
+ queryFile: 'some/path'
+ });
+
+ controller.pushObject(Ember.Object.create({
+ id: model.get('queryFile')
+ }));
+
+ equal(controller.getQueryForModel(model).get('id'), model.get('queryFile'), 'a saved record was given, the method retrieves the query by id equality with record queryFile path.');
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js
new file mode 100644
index 0000000..0ed7dd7
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/queries-test.js
@@ -0,0 +1,47 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:queries', 'QueriesController', {
+ needs: [ 'controller:history' ]
+});
+
+test('controller is initialized', function() {
+ expect(1);
+
+ var component = this.subject();
+
+ equal(component.get('columns.length'), 4, 'Columns are initialized correctly');
+});
+
+test('Should hide new queries', function() {
+ expect(1);
+
+ var queries = [
+ { isNew: true },
+ { isNew: false}
+ ];
+
+ var controller = this.subject({
+ queries: queries
+ });
+
+ equal(controller.get('model.length'), 1, 'Hide new queries from the list');
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tables.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tables.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tables.js
new file mode 100644
index 0000000..5e0f4b9
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/tables.js
@@ -0,0 +1,32 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:tables', 'TablesController', {
+ needs: ['adapter:database']
+});
+
+test('controller is initialized properly.', function () {
+ expect(1);
+
+ var controller = this.subject();
+
+ ok(controller.get('baseUrl'), 'baseUrl was set to a truthy value.');
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/udf-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/udf-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/udf-test.js
new file mode 100644
index 0000000..c7ba9e4
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/udf-test.js
@@ -0,0 +1,94 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:udf', 'UdfController', {
+ needs: [ 'controller:udfs', 'controller:file-resources' ]
+});
+
+test('controller is initialized', function() {
+ expect(2);
+
+ var model = Ember.Object.create({
+ id: 1,
+ name: 'udfmodel',
+ classname: 'cls',
+ isNew: true,
+ fileResource: 'fileresource'
+ });
+
+ var component = this.subject({ model: model});
+
+ ok(component.get('isEditing'), 'Model is new, isEditing is set to true');
+ equal(component.get('fileBackup'), model.get('fileResource'), 'fileBackup is set to model.fileResource');
+});
+
+test('addFileResource', function() {
+ expect(3);
+
+ var model = Ember.Object.create({
+ id: 1,
+ name: 'udfmodel',
+ classname: 'cls',
+ isNew: true,
+ fileResource: null
+ });
+
+ var store = {
+ createRecord: function() {
+ return 'newfileresource';
+ }
+ };
+
+ var component = this.subject({model: model, store: store });
+
+ equal(component.get('model.fileResource'), null, 'fileResource is set to null');
+
+ Ember.run(function () {
+ component.send('addFileResource');
+ });
+
+ ok(component.get('isEditingResource'), 'isEditingResource is set to true');
+ equal(component.get('model.fileResource'), store.createRecord(), 'New file resource is set on the model');
+});
+
+test('editFileResource', function() {
+ expect(3);
+
+ var model = Ember.Object.create({
+ id: 1,
+ name: 'udfmodel',
+ classname: 'cls',
+ isNew: true,
+ fileResource: null
+ });
+
+ var file = 'fileresource';
+ var component = this.subject({model: model });
+
+ equal(component.get('model.fileResource'), null, 'fileResource is set to null');
+
+ Ember.run(function () {
+ component.send('editFileResource', file);
+ });
+
+ ok(component.get('isEditingResource'), 'isEditingResource is set to true');
+ equal(component.get('model.fileResource'), file, 'New file resource is set on the model');
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/udfs-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/udfs-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/udfs-test.js
new file mode 100644
index 0000000..4160585
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/controllers/udfs-test.js
@@ -0,0 +1,62 @@
+/**
+ * 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 Ember from 'ember';
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:udfs', 'UdfsController', {
+ needs: [ ]
+});
+
+test('controller is initialized', function() {
+ expect(3);
+
+ var component = this.subject();
+
+ equal(component.get('columns.length'), 2, 'Columns are initialized correctly');
+ ok(component.get('sortAscending'), 'Sort ascending is true');
+ equal(component.get('sortProperties.length'), 0, 'sortProperties is empty');
+});
+
+test('sort', function() {
+ expect(2);
+
+ var component = this.subject();
+
+ Ember.run(function () {
+ component.send('sort', 'prop');
+ });
+
+ ok(component.get('sortAscending'), 'New sort prop sortAscending is set to true');
+ equal(component.get('sortProperties').objectAt(0), "prop", 'sortProperties is set to prop');
+});
+
+test('add', function() {
+ expect(1);
+
+ var store = {
+ createRecord: function(name) {
+ ok(name, 'store.createRecord called');
+ }
+ };
+ var component = this.subject({ store: store });
+
+ Ember.run(function () {
+ component.send('add');
+ });
+});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/helpers/path-binding-test.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/helpers/path-binding-test.js b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/helpers/path-binding-test.js
new file mode 100644
index 0000000..22ba58a
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/tests/unit/helpers/path-binding-test.js
@@ -0,0 +1,35 @@
+/**
+ * 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 {
+ pathBinding
+} from 'hive/helpers/path-binding';
+
+import Ember from 'ember';
+
+module('PathBindingHelper');
+
+// Replace this with your real tests.
+test('it should retrieve property value for a given object.', function() {
+ var obj = Ember.Object.extend({
+ name: 'some name'
+ }).create();
+
+ var result = pathBinding(obj, 'name');
+ equal(result, obj.get('name'));
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/vendor/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/vendor/.gitkeep b/contrib/views/hive/src/main/resources/ui/hive-web/vendor/.gitkeep
new file mode 100644
index 0000000..e69de29
[06/12] ambari git commit: AMBARI-9676. Initial Hive View Submission
to Contrib (alexantonenko)
Posted by al...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js
new file mode 100644
index 0000000..8def09e
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/application.js
@@ -0,0 +1,62 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Route.extend({
+ setupController: function() {
+ var self = this;
+
+ this.controllerFor(constants.namingConventions.databases).set('model', this.store.find(constants.namingConventions.database));
+
+ this.store.find(constants.namingConventions.udf).then(function (udfs) {
+ self.controllerFor(constants.namingConventions.udfs).set('udfs', udfs);
+ });
+ },
+
+ actions: {
+ openModal: function(modalTemplate, options) {
+ this.controllerFor(modalTemplate).setProperties({
+ heading: options.heading,
+ text: options.text,
+ defer: options.defer
+ });
+
+ return this.render(modalTemplate, {
+ into: 'application',
+ outlet: 'modal'
+ });
+ },
+
+ closeModal: function () {
+ return this.disconnectOutlet({
+ outlet: 'modal',
+ parentView: 'application'
+ });
+ },
+
+ addAlert: function (type, message, title) {
+ this.controllerFor(constants.namingConventions.alerts).pushObject({
+ type: type,
+ title: title,
+ content: message
+ });
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/history.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/history.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/history.js
new file mode 100644
index 0000000..7f73f8f
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/history.js
@@ -0,0 +1,30 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Route.extend({
+ model: function () {
+ return this.store.find(constants.namingConventions.job);
+ },
+
+ setupController: function (controller, model) {
+ controller.set('history', model);
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/explain.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/explain.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/explain.js
new file mode 100644
index 0000000..742f7a8
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/explain.js
@@ -0,0 +1,28 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Route.extend({
+ setupController: function (controller, model) {
+ this.controllerFor(constants.namingConventions.openQueries).updateTabSubroute(model, constants.namingConventions.subroutes.jobExplain);
+
+ this.controllerFor(constants.namingConventions.routes.index).set('model', model);
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/index.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/index.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/index.js
new file mode 100644
index 0000000..be5d1f9
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/index.js
@@ -0,0 +1,37 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Route.extend({
+ setupController: function (controller, model) {
+ var subroute;
+ var existingTab = this.controllerFor(constants.namingConventions.openQueries).getTabForModel(model);
+
+ if (existingTab) {
+ subroute = existingTab.get('subroute');
+ }
+
+ if (subroute) {
+ this.transitionTo(subroute, model);
+ } else {
+ this.transitionTo(constants.namingConventions.subroutes.jobLogs, model);
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/logs.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/logs.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/logs.js
new file mode 100644
index 0000000..954f725
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/logs.js
@@ -0,0 +1,28 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Route.extend({
+ setupController: function (controller, model) {
+ this.controllerFor(constants.namingConventions.openQueries).updateTabSubroute(model, constants.namingConventions.subroutes.jobLogs);
+
+ this.controllerFor(constants.namingConventions.routes.index).set('model', model);
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/results.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/results.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/results.js
new file mode 100644
index 0000000..f1593c5
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/history-query/results.js
@@ -0,0 +1,28 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Route.extend({
+ setupController: function (controller, model) {
+ this.controllerFor(constants.namingConventions.openQueries).updateTabSubroute(model, constants.namingConventions.subroutes.jobResults);
+
+ this.controllerFor(constants.namingConventions.routes.index).set('model', model);
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js
new file mode 100644
index 0000000..f2be946
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/index.js
@@ -0,0 +1,36 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Route.extend({
+ beforeModel: function () {
+ var model = this.controllerFor(constants.namingConventions.routes.index).get('model');
+
+ if (model) {
+ if (model.get('constructor.typeKey') === constants.namingConventions.job) {
+ this.transitionTo(constants.namingConventions.subroutes.historyQuery, model);
+ } else {
+ this.transitionTo(constants.namingConventions.subroutes.savedQuery, model);
+ }
+ } else {
+ this.controllerFor(constants.namingConventions.routes.index).send('addQuery');
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/saved-query.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/saved-query.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/saved-query.js
new file mode 100644
index 0000000..e64c522
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/index/saved-query.js
@@ -0,0 +1,33 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Route.extend({
+ setupController: function (controller, model) {
+ this.controllerFor(constants.namingConventions.routes.index).set('model', model);
+ },
+
+ actions: {
+ error: function() {
+ this.store.unloadAll(constants.namingConventions.savedQuery);
+ this.transitionTo(constants.namingConventions.routes.index);
+ }
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/loading.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/loading.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/loading.js
new file mode 100644
index 0000000..8719170
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/loading.js
@@ -0,0 +1,22 @@
+/**
+ * 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 Ember from 'ember';
+
+export default Ember.Route.extend({
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/queries.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/queries.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/queries.js
new file mode 100644
index 0000000..a10bbd7
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/queries.js
@@ -0,0 +1,30 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Route.extend({
+ model: function () {
+ return this.store.find(constants.namingConventions.savedQuery);
+ },
+
+ setupController: function (controller, model) {
+ controller.set('queries', model);
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/udfs.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/udfs.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/udfs.js
new file mode 100644
index 0000000..466f632
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/routes/udfs.js
@@ -0,0 +1,30 @@
+/**
+ * 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 Ember from 'ember';
+import constants from 'hive/utils/constants';
+
+export default Ember.Route.extend({
+ setupController: function () {
+ var self = this;
+
+ this.store.find(constants.namingConventions.fileResource).then(function (fileResources) {
+ self.controllerFor(constants.namingConventions.fileResources).set('model', fileResources);
+ });
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/serializers/database.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/serializers/database.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/serializers/database.js
new file mode 100644
index 0000000..fed6d14
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/serializers/database.js
@@ -0,0 +1,41 @@
+/**
+ * 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 DS from 'ember-data';
+
+export default DS.JSONSerializer.extend({
+ extractArray: function (store, primaryType, rawPayload) {
+ var databases = rawPayload.databases.map(function (database) {
+ return {
+ id: database,
+ name: database
+ };
+ });
+
+ var payload = { databases: databases };
+ return this._super(store, primaryType, payload);
+ },
+
+ normalizePayload: function(payload) {
+ var normalized = payload.databases.map(function(database) {
+ return database;
+ });
+
+ return this._super(normalized);
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/serializers/file.js
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/serializers/file.js b/contrib/views/hive/src/main/resources/ui/hive-web/app/serializers/file.js
new file mode 100644
index 0000000..416db0c
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/serializers/file.js
@@ -0,0 +1,23 @@
+/**
+ * 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 DS from 'ember-data';
+
+export default DS.RESTSerializer.extend({
+ primaryKey: 'filePath'
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/.gitkeep b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/.gitkeep
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss
new file mode 100644
index 0000000..6a1d35c
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/app.scss
@@ -0,0 +1,327 @@
+/**
+ * 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 'dropdown-submenu';
+
+$panel-background: #f5f5f5;
+$placeholder-color: #aaa;
+
+@-webkit-keyframes fadeIn {
+ 0% {opacity: 0;}
+ 100% {opacity: 1;}
+}
+
+@keyframes fadeIn {
+ 0% {opacity: 0;}
+ 100% {opacity: 1;}
+}
+
+.fadeIn {
+ -webkit-animation-name: fadeIn;
+ animation-name: fadeIn;
+}
+
+@-webkit-keyframes fadeOut {
+ 0% {opacity: 1;}
+ 100% {opacity: 0;}
+}
+
+@keyframes fadeOut {
+ 0% {opacity: 1;}
+ 100% {opacity: 0;}
+}
+
+.fadeOut {
+ -webkit-animation-name: fadeOut;
+ animation-name: fadeOut;
+}
+#content {
+ padding: 20px 0;
+}
+
+#alerts-container {
+ position: absolute;
+ left: 15px;
+ right: 15px;
+ z-index: 1100;
+}
+
+aside hr {
+ margin: 10px 0;
+}
+
+.halfed {
+ width: 50%;
+}
+
+.no-padding {
+ padding: 0;
+}
+
+.no-margin {
+ margin: 0;
+}
+
+.toolbox {
+ margin-top: 15px;
+
+ insert-udfs {
+ display: inline-block;
+ }
+}
+
+.form-control::-moz-placeholder {
+ color: $placeholder-color;
+}
+
+.form-control:-ms-input-placeholder {
+ color: $placeholder-color;
+}
+
+.form-control::-webkit-input-placeholder {
+ color: $placeholder-color;
+}
+
+.secondary-row {
+ background: $panel-background;
+}
+
+.btn-group {
+ .dropdown-menu {
+ right: 0;
+ left: auto;
+ }
+}
+
+.CodeMirror {
+ border: 0 1px solid #ddd;
+}
+
+.grip {
+ height: 20px;
+ border: 0 1px 1px solid #ddd;
+ background-color: $panel-background;
+ color: #bbb;
+ text-align: center;
+ font-size: inherit;
+}
+
+.slider {
+ min-width: 200px;
+}
+
+.slider-labels {
+ font-size: 10px;
+}
+
+.modal-backdrop {
+ background-color: white;
+}
+
+.Unknown {
+ color: gray;
+}
+
+.Running, .Pending, .Initialized, .fa-edit {
+ color: orange;
+}
+
+.Finished {
+ color: green;
+}
+
+.Canceled, .Error {
+ color: red;
+}
+
+dropdown .fa-remove {
+ color: red;
+}
+
+.Closed {
+ color: blue;
+}
+
+.query-container {
+ position: relative;
+ padding-right: 0;
+}
+
+.alert {
+ margin-bottom: 5px;
+ padding-bottom: 10px;
+ padding-top: 10px;
+
+ .alert-message {
+ max-height: 250px;
+ overflow-y: auto;
+ }
+}
+
+body {
+ a, i {
+ cursor: pointer; }
+
+ dropdown {
+ .btn-group, .dropdown-toggle, .dropdown-menu {
+ width: 100%;
+ }
+ }
+
+ tabs {
+ ul li{
+ background: $panel-background;
+ }
+ }
+
+ table {
+ tbody tr {
+ border: 1px solid #ccc;
+ }
+
+ td *{
+ white-space: nowrap;
+ }
+ }
+
+ column-filter {
+ display: -webkit-inline-flex; /* Safari */
+ display: inline-flex;
+
+ .slider {
+ margin-top: 10px;
+ }
+
+ .btn {
+ padding: 0;
+ border: 0;
+ }
+
+ .form-control {
+ width: auto;
+ }
+
+ .fa {
+ padding: 10px 0;
+ }
+ }
+}
+
+.CodeMirror-hints {
+ min-width: 200px;
+}
+
+.spinner {
+ width: 36px;
+ height: 36px;
+ background: url("/img/spinner.gif");
+ background-repeat: no-repeat;
+ margin: 0px auto;
+
+ &.small {
+ background-size: 20px;
+ width: 20px;
+ height: 20px;
+ }
+}
+
+.databases {
+ max-height: 500px;
+ overflow-y: auto;
+
+ .tables {
+ padding-left: 10px;
+
+ .columns {
+ padding-left: 10px;
+ }
+ }
+}
+
+.selectize-control.plugin-remove_button [data-value] {
+ padding-right: 0 !important;
+}
+
+.popover-right {
+ z-index: 92;
+ float: right;
+ position: relative;
+}
+
+.query-editor-panel .panel-body {
+ position: relative;
+}
+.settings-toggle {
+ position: absolute;
+ top: 0;
+ right: 25px;
+ cursor: pointer;
+}
+
+.settings-toggle.active {
+ color: #428bca;
+}
+.settings-container {
+ width: 100%;
+ overflow-y: scroll;
+ height: calc(100% - 41px);
+ top: 41px;
+ left: 0;
+ background-color: #fff;
+ position: absolute;
+ padding: 0 15px;
+ z-index: 1000;
+ border: 1px solid #ddd;
+
+ -webkit-animation-duration: .5s;
+ animation-duration: .5s;
+ -webkit-animation-fill-mode: both;
+ animation-fill-mode: both;
+}
+.settings-container .close-settings {
+ float: right;
+ font-size: 18px;
+ cursor: pointer;
+ line-height: 24px;
+}
+
+.setting {
+ float: left;
+ margin: 0 10px 10px 0;
+}
+
+.setting .remove {
+ line-height: 30px;
+ margin-left: 10px;
+ font-size: 18px;
+ cursor: pointer;
+}
+
+tabs {
+ display: block;
+ position: relative;
+}
+
+tree-view ul li {
+ padding-left: 10px;
+}
+
+.runOnTez {
+ float: right;
+ margin: 0
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/dropdown-submenu.scss
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/dropdown-submenu.scss b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/dropdown-submenu.scss
new file mode 100644
index 0000000..8b43bce
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/styles/dropdown-submenu.scss
@@ -0,0 +1,66 @@
+/**
+ * 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.
+ */
+
+
+.dropdown-submenu {
+ position:relative;
+}
+
+.dropdown-submenu>.dropdown-menu {
+ top:0;
+ left:100%;
+ margin-top:-6px;
+ margin-left:-1px;
+ -webkit-border-radius:0 6px 6px 6px;
+ -moz-border-radius:0 6px 6px 6px;
+ border-radius:0 6px 6px 6px;
+}
+
+.dropdown-submenu:hover>.dropdown-menu {
+ display:block;
+}
+
+.dropdown-submenu>a:after {
+ display:block;
+ content:" ";
+ float:right;
+ width:0;
+ height:0;
+ border-color:transparent;
+ border-style:solid;
+ border-width:5px 0 5px 5px;
+ border-left-color:#cccccc;
+ margin-top:5px;
+ margin-right:-10px;
+}
+
+.dropdown-submenu:hover>a:after {
+ border-left-color:#ffffff;
+}
+
+.dropdown-submenu.pull-left {
+ float:none;
+}
+
+.dropdown-submenu.pull-left>.dropdown-menu {
+ left:-100%;
+ margin-left:10px;
+ -webkit-border-radius:6px 0 6px 6px;
+ -moz-border-radius:6px 0 6px 6px;
+ border-radius:6px 0 6px 6px;
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/.gitkeep b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/.gitkeep
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/alerts.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/alerts.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/alerts.hbs
new file mode 100644
index 0000000..0fbf34a
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/alerts.hbs
@@ -0,0 +1,23 @@
+{{!
+* 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.
+}}
+
+<div id="alerts-container">
+ {{#each alert in this}}
+ {{alert-message-widget message=alert removeMessage="remove" removeLater="removeLater"}}
+ {{/each}}
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs
new file mode 100644
index 0000000..055a87b
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/application.hbs
@@ -0,0 +1,25 @@
+{{!
+* 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.
+}}
+
+{{render 'navbar'}}
+
+<div id="content">
+ {{outlet}}
+
+ {{outlet "modal"}}
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/.gitkeep b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/.gitkeep
new file mode 100644
index 0000000..e69de29
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs
new file mode 100644
index 0000000..7106f0a
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/alert-message-widget.hbs
@@ -0,0 +1,28 @@
+{{!
+* 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.
+}}
+
+<div {{bind-attr class=":alert :alert-dismissible message.typeClass"}}>
+ <button type="button" class="close" data-dismiss="alert" aria-hidden="true" {{action "remove"}}>×</button>
+ <strong>{{tb-helper message.title}}</strong>
+
+ {{#if message.isExpanded}}
+ <div class="alert-message">
+ {{message.content}}
+ </div>
+ {{/if}}
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs
new file mode 100644
index 0000000..f6af59b
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/collapsible-widget.hbs
@@ -0,0 +1,24 @@
+{{!
+* 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.
+}}
+
+<div>
+ <a {{action "toggle"}} {{bind-attr class=":fa iconClass"}}> {{heading}}</a>
+</div>
+{{#if isExpanded}}
+ {{yield}}
+{{/if}}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/column-filter-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/column-filter-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/column-filter-widget.hbs
new file mode 100644
index 0000000..72c72c1
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/column-filter-widget.hbs
@@ -0,0 +1,42 @@
+{{!
+* 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.
+}}
+
+{{#if column.dateRange}}
+ {{date-range-widget class="pull-left"
+ rangeChanged="sendFilter"
+ dateRange=column.dateRange}}
+{{else}}
+ {{#if column.numberRange}}
+ {{number-range-widget class="pull-left"
+ numberRange=column.numberRange
+ rangeChanged="sendFilter"}}
+ {{else}}
+ {{extended-input type="text"
+ class="pull-left form-control input-sm"
+ placeholderTranslation=column.caption
+ value=filterValue
+ valueChanged="sendFilter"}}
+ {{/if}}
+{{/if}}
+<span {{action "sendSort"}}>
+ {{#if isSorted}}
+ <a {{bind-attr class=":pull-right :fa sortAscending:fa-sort-asc:fa-sort-desc"}}></a>
+ {{else}}
+ <i class="pull-right fa fa-unsorted"></i>
+ {{/if}}
+</span>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/date-range-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/date-range-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/date-range-widget.hbs
new file mode 100644
index 0000000..4b9686e
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/date-range-widget.hbs
@@ -0,0 +1,22 @@
+{{!
+* 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.
+}}
+
+<div class="form-inline">
+ {{input type="text" value=displayFromDate class="input-sm form-control fromDate"}}
+ {{input type="text" value=displayToDate class="input-sm form-control toDate"}}
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/expander-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/expander-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/expander-widget.hbs
new file mode 100644
index 0000000..386556c
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/expander-widget.hbs
@@ -0,0 +1,31 @@
+{{!
+* 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.
+}}
+
+<div class="panel panel-default no-margin">
+ <div class="panel-heading accordion-heading" {{action "toggle"}}>
+ <span {{bind-attr class=":fa isExpanded:fa-caret-down:fa-caret-right"}}></span>
+ <a class="accordion-toggle">{{heading}}</a>
+ <span class="badge pull-right">{{count}}</span>
+ </div>
+
+ <div class="panel-body accordion-body collapse">
+ <div class="accordion-inner">
+ {{yield}}
+ </div>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/modal-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/modal-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/modal-widget.hbs
new file mode 100644
index 0000000..8f2dec7
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/modal-widget.hbs
@@ -0,0 +1,35 @@
+{{!
+* 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.
+}}
+
+<div class="modal fade">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close"><span aria-hidden="true" data-dismiss="modal">×</span><span class="sr-only">Close</span></button>
+ <h4 class="modal-title">{{tb-helper heading}}</h4>
+ </div>
+ <div class="modal-body">
+ {{yield}}
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-sm btn-danger" data-dismiss="modal">{{t "buttons.close"}}</button>
+ <button type="button" class="btn btn-sm btn-success" {{action 'ok'}}>{{t "buttons.ok"}}</button>
+ </div>
+ </div>
+ </div>
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/no-bubbling.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/no-bubbling.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/no-bubbling.hbs
new file mode 100644
index 0000000..6ae472f
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/no-bubbling.hbs
@@ -0,0 +1,19 @@
+{{!
+* 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.
+}}
+
+{{yield}}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/number-range-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/number-range-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/number-range-widget.hbs
new file mode 100644
index 0000000..279ec07
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/number-range-widget.hbs
@@ -0,0 +1,23 @@
+{{!
+* 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.
+}}
+
+<div class="slider"></div>
+<div class="slider-labels">
+ <span class="pull-left">{{numberRange.from}}</span>
+ <span class="pull-right">{{numberRange.to}}{{numberRange.units}}</span>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs
new file mode 100644
index 0000000..45c4edf
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/panel-widget.hbs
@@ -0,0 +1,46 @@
+{{!
+* 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.
+}}
+
+<div class="panel panel-default" {{bind-attr class=classNames}}>
+ {{#if heading}}
+ <div class="panel-heading">
+ {{#if menuItems}}
+ <div class="dropdown pull-right">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{menuHeading}} <b class="caret"></b></a>
+
+ <ul class="dropdown-menu" role="menu">
+ {{#each item in menuItems}}
+ {{#if item.href}}
+ <li><a {{bind-attr href=item.href}}>{{item.title}}</a></li>
+ {{else}}
+ <li><a {{action "sendMenuItemAction" item.action}}>{{item.title}}</a></li>
+ {{/if}}
+ {{/each}}
+ </ul>
+ </div>
+ {{/if}}
+ <strong>{{heading}}</strong>
+ {{#if isLoading}}
+ <div class="spinner small pull-right"></div>
+ {{/if}}
+ </div>
+ {{/if}}
+ <div class="panel-body">
+ {{yield}}
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/popover-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/popover-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/popover-widget.hbs
new file mode 100644
index 0000000..fee80c7
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/popover-widget.hbs
@@ -0,0 +1,19 @@
+{{!
+* 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.
+}}
+
+<div class="hide"> {{yield}} </div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/progress-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/progress-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/progress-widget.hbs
new file mode 100644
index 0000000..c89a9dd
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/progress-widget.hbs
@@ -0,0 +1,25 @@
+{{!
+* 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.
+}}
+
+<div class="progress">
+ {{#each stage in formattedStages}}
+ <div {{bind-attr class="stage.className :progress-bar"}} {{bind-attr style=stage.progress}}>
+ <span class="sr-only">35% Complete (success)</span>
+ </div>
+ {{/each}}
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/query-editor.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/query-editor.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/query-editor.hbs
new file mode 100644
index 0000000..4ec9259
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/query-editor.hbs
@@ -0,0 +1,19 @@
+{{!
+* 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.
+}}
+
+{{textarea id="code-mirror" rows="15" cols="20" value=query}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/select-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/select-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/select-widget.hbs
new file mode 100644
index 0000000..6da1f3e
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/select-widget.hbs
@@ -0,0 +1,39 @@
+{{!
+* 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.
+}}
+
+<div class="btn-group">
+ <a class="btn btn-default dropdown-toggle" data-toggle="dropdown">
+ <span class="selected-item pull-left">{{selectedLabel}}</span>
+ <span class="pull-right fa fa-caret-down"></span>
+ </a>
+ <ul class="dropdown-menu">
+ {{#each item in items}}
+ <li {{action "select" item}}>
+ <a>{{path-binding item labelPath}}
+ {{#if canEdit}}
+ <span class="fa fa-remove pull-right" {{action "remove" item}}></span>
+ <span class="fa fa-edit pull-right" {{action "edit" item}}></span>
+ {{/if}}
+ </a>
+ </li>
+ {{/each}}
+ {{#if canAdd}}
+ <li {{action "add"}}><a>{{t "buttons.addItem"}}</a></li>
+ {{/if}}
+ </ul>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs
new file mode 100644
index 0000000..e5f313a
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tabs-widget.hbs
@@ -0,0 +1,40 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<ul class="nav nav-tabs">
+ {{#each tab in tabs}}
+ {{#if tab.visible}}
+ {{#if tab.path}}
+ {{#link-to tab.path tab.id tagName="li"}}
+ <a>
+ {{tab.name}}
+ {{#if view.removeEnabled}}
+ <i class="fa fa-remove" {{action 'remove' tab}}></i>
+ {{/if}}
+ </a>
+ {{/link-to}}
+ {{else}}
+ <li {{bind-attr class="tab.active:active"}} {{action 'selectTab' tab}}>
+ <a>{{tab.name}}</a>
+ </li>
+ {{/if}}
+ {{/if}}
+ {{/each}}
+</ul>
+
+{{yield}}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tree-view.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tree-view.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tree-view.hbs
new file mode 100644
index 0000000..cb83ec3
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/tree-view.hbs
@@ -0,0 +1,28 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<ul class="list-unstyled">
+ {{#each item in content}}
+ <li>
+ {{item.text}}
+ {{#if item.contents}}
+ {{tree-view content=item.contents}}
+ {{/if}}
+ </li>
+ {{/each}}
+</ul>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/typeahead-widget.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/typeahead-widget.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/typeahead-widget.hbs
new file mode 100644
index 0000000..4083ad6
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/components/typeahead-widget.hbs
@@ -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.
+}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs
new file mode 100644
index 0000000..38f6b66
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-search-results.hbs
@@ -0,0 +1,48 @@
+{{!
+* 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.
+}}
+
+{{#if tableSearchResults.tables}}
+ <div class="databases">
+ <a class="fa fa-database"> {{selectedDatabase.name}}</a>
+
+ <div class="tables">
+ {{#each table in tableSearchResults.tables}}
+ <a class="fa fa-th"> {{table.name}}</a>
+
+ <div class="columns">
+ {{#each column in table.columns}}
+ <div>
+ <strong>{{column.name}}</strong>
+ <span class="pull-right">{{column.type}}</span>
+ </div>
+ {{/each}}
+
+ {{#if table.hasNext}}
+ <strong><a {{action "showMoreResultColumns" table}}>{{t "buttons.loadMore"}}</a></strong>
+ {{/if}}
+ </div>
+ {{/each}}
+
+ {{#if tableSearchResults.hasNext}}
+ <strong><a {{action "showMoreResultTables" database}}>{{t "buttons.loadMore"}}</a></strong>
+ {{/if}}
+ </div>
+ </div>
+{{else}}
+ <h4>{{t "labels.noTablesMatches"}} "{{tablesSearchTerm}}"</h4>
+{{/if}}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs
new file mode 100644
index 0000000..5da9760
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases-tree.hbs
@@ -0,0 +1,48 @@
+{{!
+* 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.
+}}
+
+<div class="databases">
+ {{#each database in content}}
+ {{#collapsible-widget heading=database.name isExpanded=database.isExpanded iconClass="fa-database" expanded="getTables"}}
+ {{#if database.isExpanded}}
+ <div class="tables">
+ {{#each table in database.visibleTables}}
+ {{#collapsible-widget heading=table.name isExpanded=table.isExpanded toggledParam=database iconClass="fa-th" expanded="getColumns"}}
+ {{#if table.isExpanded}}
+ <div class="columns">
+ {{#each column in table.visibleColumns}}
+ <div>
+ <strong>{{column.name}}</strong>
+ <span class="pull-right">{{column.type}}</span>
+ </div>
+ {{/each}}
+ {{#if table.canGetNextPage}}
+ <strong><a {{action "showMoreColumns" table database}}>{{t "buttons.loadMore"}}</a></strong>
+ {{/if}}
+ </div>
+ {{/if}}
+ {{/collapsible-widget}}
+ {{/each}}
+ {{#if database.canGetNextPage}}
+ <strong><a {{action "showMoreTables" database}}>{{t "buttons.loadMore"}}</a></strong>
+ {{/if}}
+ </div>
+ {{/if}}
+ {{/collapsible-widget}}
+ {{/each}}
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs
new file mode 100644
index 0000000..115bd70
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/databases.hbs
@@ -0,0 +1,54 @@
+{{!
+* 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.
+}}
+
+{{#panel-widget headingTranslation="titles.database" isLoading=isLoading classNames="database-explorer"}}
+ {{#if model}}
+
+ {{typeahead-widget
+ content=model
+ optionValuePath="content.id"
+ optionLabelPath="content.name"
+ selection=selectedDatabase
+ }}
+
+ <hr />
+
+ {{#if selectedDatabase}}
+ {{extended-input class="form-control input-sm"
+ placeholderTranslation="placeholders.search.tables"
+ valueSearched="searchTables"
+ value=tableSearchTerm}}
+
+ <hr />
+ {{/if}}
+
+
+ {{#if tableSearchResults.tables}}
+ {{extended-input class="form-control input-sm"
+ placeholderTranslation="placeholders.search.columns"
+ valueSearched="searchColumns"
+ value=columnSearchTerm}}
+
+ <hr />
+ {{/if}}
+
+ {{#tabs-widget tabs=tabs selectedTab=selectedTab}}
+ {{partial selectedTab.view}}
+ {{/tabs-widget}}
+ {{/if}}
+{{/panel-widget}}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/history.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/history.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/history.hbs
new file mode 100644
index 0000000..22c2ff3
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/history.hbs
@@ -0,0 +1,70 @@
+{{!
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+}}
+
+<table class="table table-expandable">
+ <thead>
+ <tr>
+ {{#each column in columns}}
+ <th>
+ {{#if column.caption}}
+ {{column-filter-widget class="pull-left"
+ column=column
+ filterValue=column.filterValue
+ sortAscending=controller.sortAscending
+ sortProperties=controller.sortProperties
+ columnSorted="sort"
+ columnFiltered="filter"}}
+ {{else}}
+ {{tb-helper "caption" column}}
+ {{/if}}
+ </th>
+ {{/each}}
+ <th>
+ <button type="btn" class="btn btn-sm btn-warning pull-right clear-filters" {{action "clearFilters"}}>{{t "buttons.clearFilters"}}</button>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ {{#each item in this}}
+ <tr class="main-row" {{action "loadFile"}}>
+ <td>
+ {{#link-to "index.historyQuery" item}}
+ {{item.title}}
+ {{/link-to}}
+ </td>
+ <td {{bind-attr class=item.status}}>{{item.status}}</td>
+ <td>{{date-binding item "dateSubmitted"}}</td>
+ <td>{{item.duration}}</td>
+ <td>
+ <a class="fa fa-expand pull-right"></a>
+ </td>
+ </tr>
+ {{#if controller.expanded}}
+ <tr class="secondary-row">
+ <td {{bind-attr colspan=view.colspan}}>
+ {{code-helper controller.file.fileContent}}
+
+ {{#if controller.canStop}}
+ <button type="button" class="btn btn-danger btn-sm pull-right" {{action "stop"}}>{{t "buttons.stopJob"}}</button>
+ {{/if}}
+ </td>
+ </tr>
+ {{/if}}
+ {{/each}}
+ </tbody>
+</table>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs
new file mode 100644
index 0000000..622af66
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index.hbs
@@ -0,0 +1,82 @@
+{{!
+* 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.
+}}
+
+<aside class="col-md-3 col-xs-12 pull-left no-padding">
+ {{render 'databases'}}
+</aside>
+
+<div class="col-md-9 col-xs 12 pull-left query-container">
+
+ {{render 'alerts'}}
+ {{#panel-widget headingTranslation="titles.query.editor" classNames="query-editor-panel"}}
+ {{#popover-widget classNames="fa fa-info-circle popover-right" titleTranslation="popover.queryEditorHelp.title" }}
+ <ul>
+ <li>{{t 'popover.queryEditorHelp.content.line1'}}</li>
+ <li>{{t 'popover.queryEditorHelp.content.line2'}}</li>
+ <li>{{t 'popover.queryEditorHelp.content.line3'}}</li>
+ </ul>
+ {{/popover-widget}}
+
+ {{render 'open-queries'}}
+
+ <div class="toolbox">
+ <button type="button" class="btn btn-sm btn-success execute-query"
+ {{bind-attr class="canExecute::disabled"}}
+ {{action "executeQuery"}}>
+ {{t "buttons.execute"}}
+ </button>
+ <button type="button" class="btn btn-sm btn-default"
+ {{bind-attr class="canExecute::disabled"}}
+ {{action "explainQuery"}}>
+ {{t "buttons.explain"}}
+ </button>
+ <button type="button" class="btn btn-sm btn-default save-query-as" {{action "saveQuery"}}>{{t "buttons.saveAs"}}</button>
+
+ {{render 'insert-udfs'}}
+
+ <button type="button" class="btn btn-sm btn-primary pull-right" {{action "addQuery"}}>{{t "buttons.newQuery"}}</button>
+ </div>
+ {{/panel-widget}}
+
+ {{#if queryParams}}
+ {{#panel-widget headingTranslation="titles.query.parameters"}}
+ <div class="form-horizontal">
+ {{#each param in queryParams}}
+ <div {{bind-attr class=":form-group param.value:has-success:has-error"}}>
+ <label class="col-sm-3 control-label">{{param.name}}</label>
+ <div class="col-sm-9">
+ {{input value=param.value placeholder="value" class="form-control"}}
+ </div>
+ </div>
+ {{/each}}
+ </div>
+ {{/panel-widget}}
+ {{/if}}
+
+ {{#if displayJobTabs}}
+ {{#panel-widget headingTranslation="titles.query.process"
+ isLoading=model.isRunning
+ menuItems=downloadMenu
+ menuHeadingTranslation="titles.download"
+ classNames="query-process-results-panel"}}
+ {{#tabs-widget tabs=queryProcessTabs selectedTab=selectedQueryProcessTab}}
+ {{outlet}}
+ {{/tabs-widget}}
+ {{/panel-widget}}
+ {{/if}}
+</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/explain.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/explain.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/explain.hbs
new file mode 100644
index 0000000..3f8810f
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/explain.hbs
@@ -0,0 +1,23 @@
+{{!
+* 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.
+}}
+
+{{#each header in formattedExplain}}
+ {{#expander-widget heading=header.text}}
+ {{tree-view content=header.contents}}
+ {{/expander-widget}}
+{{/each}}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/logs.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/logs.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/logs.hbs
new file mode 100644
index 0000000..283d929
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/logs.hbs
@@ -0,0 +1,19 @@
+{{!
+* 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.
+}}
+
+{{log-helper model.log}}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs
new file mode 100644
index 0000000..ed6fba6
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/index/history-query/results.hbs
@@ -0,0 +1,47 @@
+{{!
+* 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.
+}}
+
+{{#if results}}
+ <table class="table table-expandable">
+ <thead>
+ <tr>
+ {{#each column in results.schema}}
+ <th> {{column.name}} </th>
+ {{/each}}
+ </tr>
+ </thead>
+ <tbody>
+ {{#each row in results.rows}}
+ <tr>
+ {{#each item in row}}
+ <td>{{item}}</td>
+ {{/each}}
+ </tr>
+ {{/each}}
+ </tbody>
+ </table>
+
+ <div class="pull-right">
+ <button type="button" {{action "getPreviousPage"}}
+ {{bind-attr class=":btn :btn-sm :btn-default disablePrevious:disabled"}}>{{t "buttons.previousPage"}}</button>
+ <button type="button" {{action "getNextPage"}}
+ {{bind-attr class=":btn :btn-sm :btn-default disableNext:disabled"}}>{{t "buttons.nextPage"}}</button>
+ </div>
+{{else}}
+ {{error}}
+{{/if}}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs
new file mode 100644
index 0000000..0ea2c99
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/insert-udfs.hbs
@@ -0,0 +1,44 @@
+{{!
+* 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.
+}}
+
+<div class="dropdown">
+ <a role="button" data-toggle="dropdown" class="btn btn-default btn-sm" data-target="#">
+ {{t "placeholders.select.udfs"}}
+ <span class="caret"></span>
+ </a>
+ <ul class="dropdown-menu pull-right" role="menu" aria-labelledby="dropdownMenu">
+ {{#each item in this}}
+ <li class="dropdown dropdown-submenu">
+ {{#if item.file}}
+ <a tabindex="-1">{{item.file.name}}</a>
+ {{else}}
+ <a tabindex="-1">{{tb-helper item.name}}</a>
+ {{/if}}
+ <ul class="dropdown-menu">
+ {{#each udf in item.udfs}}
+ <li>
+ {{#no-bubbling click="insertUdf" data=udf tagName="a"}}
+ {{udf.name}}
+ {{/no-bubbling}}
+ </li>
+ {{/each}}
+ </ul>
+ </li>
+ {{/each}}
+ </ul>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/loading.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/loading.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/loading.hbs
new file mode 100644
index 0000000..c0de275
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/loading.hbs
@@ -0,0 +1,19 @@
+{{!
+* 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.
+}}
+
+<div class="spinner"></div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/logs.hbs
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/logs.hbs b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/logs.hbs
new file mode 100644
index 0000000..283d929
--- /dev/null
+++ b/contrib/views/hive/src/main/resources/ui/hive-web/app/templates/logs.hbs
@@ -0,0 +1,19 @@
+{{!
+* 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.
+}}
+
+{{log-helper model.log}}
\ No newline at end of file
[12/12] ambari git commit: AMBARI-9676. Initial Hive View Submission
to Contrib (alexantonenko)
Posted by al...@apache.org.
AMBARI-9676. Initial Hive View Submission to Contrib (alexantonenko)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/f5482d89
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/f5482d89
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/f5482d89
Branch: refs/heads/branch-2.0.0
Commit: f5482d89da0481f642dda58e7dae4e0fe76d64bc
Parents: 828d85d
Author: Alex Antonenko <hi...@gmail.com>
Authored: Tue Feb 24 18:28:59 2015 +0200
Committer: Alex Antonenko <hi...@gmail.com>
Committed: Tue Feb 24 18:31:21 2015 +0200
----------------------------------------------------------------------
contrib/views/hive/pom.xml | 306 ++++++++++++
.../apache/ambari/view/hive/BaseService.java | 58 +++
.../apache/ambari/view/hive/HelpService.java | 72 +++
.../org/apache/ambari/view/hive/TestBean.java | 36 ++
.../backgroundjobs/BackgroundJobController.java | 80 ++++
.../view/hive/client/ColumnDescription.java | 30 ++
.../hive/client/ColumnDescriptionExtended.java | 103 ++++
.../hive/client/ColumnDescriptionShort.java | 72 +++
.../ambari/view/hive/client/Connection.java | 401 ++++++++++++++++
.../ambari/view/hive/client/ConnectionPool.java | 81 ++++
.../apache/ambari/view/hive/client/Cursor.java | 233 +++++++++
.../ambari/view/hive/client/DDLDelegator.java | 140 ++++++
.../ambari/view/hive/client/HiveCall.java | 68 +++
.../view/hive/client/HiveClientException.java | 25 +
.../hive/client/HiveClientRuntimeException.java | 25 +
.../hive/client/HiveErrorStatusException.java | 30 ++
.../ambari/view/hive/client/LogsCursor.java | 36 ++
.../org/apache/ambari/view/hive/client/Row.java | 50 ++
.../apache/ambari/view/hive/client/Utils.java | 72 +++
.../view/hive/client/ViewSessionState.java | 32 ++
.../view/hive/persistence/DataStoreStorage.java | 186 ++++++++
.../persistence/InstanceKeyValueStorage.java | 140 ++++++
.../view/hive/persistence/KeyValueStorage.java | 162 +++++++
.../hive/persistence/LocalKeyValueStorage.java | 78 +++
.../persistence/PersistentConfiguration.java | 52 ++
.../ambari/view/hive/persistence/Storage.java | 86 ++++
.../utils/ContextConfigurationAdapter.java | 260 ++++++++++
.../persistence/utils/FilteringStrategy.java | 31 ++
.../view/hive/persistence/utils/Indexed.java | 36 ++
.../hive/persistence/utils/ItemNotFound.java | 25 +
.../utils/OnlyOwnersFilteringStrategy.java | 33 ++
.../view/hive/persistence/utils/Owned.java | 36 ++
.../persistence/utils/PersonalResource.java | 22 +
.../hive/persistence/utils/StorageUtil.java | 94 ++++
.../hive/resources/CRUDResourceManager.java | 133 ++++++
.../resources/PersonalCRUDResourceManager.java | 109 +++++
.../resources/SharedCRUDResourceManager.java | 50 ++
.../resources/browser/HiveBrowserService.java | 263 ++++++++++
.../view/hive/resources/files/FileResource.java | 70 +++
.../view/hive/resources/files/FileService.java | 185 ++++++++
.../resources/jobs/ConnectionController.java | 62 +++
.../hive/resources/jobs/FileResourceShort.java | 55 +++
.../ambari/view/hive/resources/jobs/Job.java | 90 ++++
.../view/hive/resources/jobs/JobController.java | 46 ++
.../resources/jobs/JobControllerFactory.java | 43 ++
.../hive/resources/jobs/JobControllerImpl.java | 326 +++++++++++++
.../view/hive/resources/jobs/JobImpl.java | 203 ++++++++
.../hive/resources/jobs/JobResourceManager.java | 107 +++++
.../resources/jobs/JobResourceProvider.java | 108 +++++
.../view/hive/resources/jobs/JobService.java | 355 ++++++++++++++
.../view/hive/resources/jobs/LogParser.java | 113 +++++
.../jobs/ModifyNotificationDelegate.java | 23 +
.../ModifyNotificationInvocationHandler.java | 40 ++
.../jobs/NoOperationStatusSetException.java | 26 +
.../jobs/OperationHandleController.java | 113 +++++
.../jobs/OperationHandleControllerFactory.java | 52 ++
.../jobs/OperationHandleResourceManager.java | 85 ++++
.../jobs/ResultsPaginationController.java | 180 +++++++
.../resources/jobs/StoredOperationHandle.java | 146 ++++++
.../resources/resources/FileResourceItem.java | 78 +++
.../resources/FileResourceResourceManager.java | 64 +++
.../resources/FileResourceResourceProvider.java | 107 +++++
.../resources/FileResourceService.java | 181 +++++++
.../hive/resources/savedQueries/SavedQuery.java | 96 ++++
.../savedQueries/SavedQueryResourceManager.java | 132 ++++++
.../SavedQueryResourceProvider.java | 104 ++++
.../savedQueries/SavedQueryService.java | 184 +++++++
.../ambari/view/hive/resources/udfs/UDF.java | 87 ++++
.../hive/resources/udfs/UDFResourceManager.java | 64 +++
.../resources/udfs/UDFResourceProvider.java | 107 +++++
.../view/hive/resources/udfs/UDFService.java | 196 ++++++++
.../utils/BadRequestFormattedException.java | 27 ++
.../ambari/view/hive/utils/FilePaginator.java | 127 +++++
.../apache/ambari/view/hive/utils/HdfsApi.java | 399 ++++++++++++++++
.../apache/ambari/view/hive/utils/HdfsUtil.java | 87 ++++
.../MisconfigurationFormattedException.java | 47 ++
.../hive/utils/NotFoundFormattedException.java | 27 ++
.../hive/utils/ServiceFormattedException.java | 78 +++
.../src/main/resources/ui/hive-web/.gitignore | 35 ++
.../src/main/resources/ui/hive-web/.jshintrc | 32 ++
.../src/main/resources/ui/hive-web/Brocfile.js | 46 ++
.../src/main/resources/ui/hive-web/README.md | 14 +
.../ui/hive-web/app/adapters/application.js | 44 ++
.../ui/hive-web/app/adapters/database.js | 25 +
.../resources/ui/hive-web/app/adapters/file.js | 26 +
.../src/main/resources/ui/hive-web/app/app.js | 34 ++
.../ui/hive-web/app/components/.gitkeep | 0
.../app/components/alert-message-widget.js | 35 ++
.../app/components/collapsible-widget.js | 33 ++
.../app/components/column-filter-widget.js | 56 +++
.../app/components/date-range-widget.js | 62 +++
.../hive-web/app/components/expander-widget.js | 30 ++
.../hive-web/app/components/extended-input.js | 50 ++
.../ui/hive-web/app/components/modal-widget.js | 34 ++
.../ui/hive-web/app/components/no-bubbling.js | 31 ++
.../app/components/number-range-widget.js | 52 ++
.../ui/hive-web/app/components/panel-widget.js | 30 ++
.../hive-web/app/components/popover-widget.js | 33 ++
.../hive-web/app/components/progress-widget.js | 45 ++
.../ui/hive-web/app/components/query-editor.js | 121 +++++
.../ui/hive-web/app/components/select-widget.js | 66 +++
.../ui/hive-web/app/components/tabs-widget.js | 64 +++
.../ui/hive-web/app/components/tree-view.js | 23 +
.../hive-web/app/components/typeahead-widget.js | 44 ++
.../ui/hive-web/app/controllers/.gitkeep | 0
.../ui/hive-web/app/controllers/alerts.js | 47 ++
.../ui/hive-web/app/controllers/columns.js | 104 ++++
.../ui/hive-web/app/controllers/databases.js | 354 ++++++++++++++
.../hive-web/app/controllers/file-resources.js | 22 +
.../ui/hive-web/app/controllers/history.js | 148 ++++++
.../ui/hive-web/app/controllers/index.js | 475 +++++++++++++++++++
.../controllers/index/history-query/explain.js | 119 +++++
.../app/controllers/index/history-query/logs.js | 95 ++++
.../controllers/index/history-query/results.js | 134 ++++++
.../ui/hive-web/app/controllers/insert-udfs.js | 58 +++
.../ui/hive-web/app/controllers/job.js | 49 ++
.../ui/hive-web/app/controllers/loaded-files.js | 65 +++
.../ui/hive-web/app/controllers/modal-delete.js | 33 ++
.../ui/hive-web/app/controllers/modal-save.js | 33 ++
.../ui/hive-web/app/controllers/open-queries.js | 320 +++++++++++++
.../ui/hive-web/app/controllers/queries.js | 107 +++++
.../ui/hive-web/app/controllers/settings.js | 150 ++++++
.../ui/hive-web/app/controllers/tables.js | 93 ++++
.../ui/hive-web/app/controllers/udf.js | 138 ++++++
.../ui/hive-web/app/controllers/udfs.js | 69 +++
.../resources/ui/hive-web/app/helpers/.gitkeep | 0
.../ui/hive-web/app/helpers/code-helper.js | 28 ++
.../ui/hive-web/app/helpers/date-binding.js | 27 ++
.../ui/hive-web/app/helpers/log-helper.js | 28 ++
.../ui/hive-web/app/helpers/path-binding.js | 29 ++
.../ui/hive-web/app/helpers/tb-helper.js | 33 ++
.../main/resources/ui/hive-web/app/index.html | 38 ++
.../ui/hive-web/app/initializers/i18n.js | 160 +++++++
.../ui/hive-web/app/mixins/filterable.js | 104 ++++
.../ui/hive-web/app/mixins/sortable.js | 31 ++
.../resources/ui/hive-web/app/models/.gitkeep | 0
.../ui/hive-web/app/models/database.js | 25 +
.../ui/hive-web/app/models/file-resource.js | 25 +
.../resources/ui/hive-web/app/models/file.js | 26 +
.../resources/ui/hive-web/app/models/job.js | 32 ++
.../ui/hive-web/app/models/saved-query.js | 29 ++
.../resources/ui/hive-web/app/models/udf.js | 27 ++
.../main/resources/ui/hive-web/app/router.js | 47 ++
.../resources/ui/hive-web/app/routes/.gitkeep | 0
.../ui/hive-web/app/routes/application.js | 62 +++
.../resources/ui/hive-web/app/routes/history.js | 30 ++
.../app/routes/index/history-query/explain.js | 28 ++
.../app/routes/index/history-query/index.js | 37 ++
.../app/routes/index/history-query/logs.js | 28 ++
.../app/routes/index/history-query/results.js | 28 ++
.../ui/hive-web/app/routes/index/index.js | 36 ++
.../ui/hive-web/app/routes/index/saved-query.js | 33 ++
.../resources/ui/hive-web/app/routes/loading.js | 22 +
.../resources/ui/hive-web/app/routes/queries.js | 30 ++
.../resources/ui/hive-web/app/routes/udfs.js | 30 ++
.../ui/hive-web/app/serializers/database.js | 41 ++
.../ui/hive-web/app/serializers/file.js | 23 +
.../resources/ui/hive-web/app/styles/.gitkeep | 0
.../resources/ui/hive-web/app/styles/app.scss | 327 +++++++++++++
.../hive-web/app/styles/dropdown-submenu.scss | 66 +++
.../ui/hive-web/app/templates/.gitkeep | 0
.../ui/hive-web/app/templates/alerts.hbs | 23 +
.../ui/hive-web/app/templates/application.hbs | 25 +
.../hive-web/app/templates/components/.gitkeep | 0
.../components/alert-message-widget.hbs | 28 ++
.../templates/components/collapsible-widget.hbs | 24 +
.../components/column-filter-widget.hbs | 42 ++
.../templates/components/date-range-widget.hbs | 22 +
.../templates/components/expander-widget.hbs | 31 ++
.../app/templates/components/modal-widget.hbs | 35 ++
.../app/templates/components/no-bubbling.hbs | 19 +
.../components/number-range-widget.hbs | 23 +
.../app/templates/components/panel-widget.hbs | 46 ++
.../app/templates/components/popover-widget.hbs | 19 +
.../templates/components/progress-widget.hbs | 25 +
.../app/templates/components/query-editor.hbs | 19 +
.../app/templates/components/select-widget.hbs | 39 ++
.../app/templates/components/tabs-widget.hbs | 40 ++
.../app/templates/components/tree-view.hbs | 28 ++
.../templates/components/typeahead-widget.hbs | 17 +
.../app/templates/databases-search-results.hbs | 48 ++
.../hive-web/app/templates/databases-tree.hbs | 48 ++
.../ui/hive-web/app/templates/databases.hbs | 54 +++
.../ui/hive-web/app/templates/history.hbs | 70 +++
.../ui/hive-web/app/templates/index.hbs | 82 ++++
.../templates/index/history-query/explain.hbs | 23 +
.../app/templates/index/history-query/logs.hbs | 19 +
.../templates/index/history-query/results.hbs | 47 ++
.../ui/hive-web/app/templates/insert-udfs.hbs | 44 ++
.../ui/hive-web/app/templates/loading.hbs | 19 +
.../ui/hive-web/app/templates/logs.hbs | 19 +
.../ui/hive-web/app/templates/modal-delete.hbs | 21 +
.../ui/hive-web/app/templates/modal-save.hbs | 21 +
.../ui/hive-web/app/templates/navbar.hbs | 45 ++
.../ui/hive-web/app/templates/open-queries.hbs | 23 +
.../ui/hive-web/app/templates/queries.hbs | 78 +++
.../ui/hive-web/app/templates/redirect.hbs | 19 +
.../ui/hive-web/app/templates/settings.hbs | 52 ++
.../ui/hive-web/app/templates/udfs.hbs | 112 +++++
.../ui/hive-web/app/transforms/date.js | 49 ++
.../ui/hive-web/app/utils/constants.js | 122 +++++
.../ui/hive-web/app/utils/functions.js | 36 ++
.../resources/ui/hive-web/app/views/.gitkeep | 0
.../resources/ui/hive-web/app/views/history.js | 25 +
.../ui/hive-web/app/views/insert-udfs.js | 23 +
.../resources/ui/hive-web/app/views/navbar.js | 38 ++
.../main/resources/ui/hive-web/big_tables.js | 54 +++
.../src/main/resources/ui/hive-web/bower.json | 27 ++
.../resources/ui/hive-web/config/environment.js | 65 +++
.../src/main/resources/ui/hive-web/package.json | 45 ++
.../src/main/resources/ui/hive-web/testem.json | 11 +
.../main/resources/ui/hive-web/tests/.jshintrc | 74 +++
.../ui/hive-web/tests/blanket-options.js | 25 +
.../ui/hive-web/tests/helpers/api-mock.js | 242 ++++++++++
.../ui/hive-web/tests/helpers/resolver.js | 29 ++
.../ui/hive-web/tests/helpers/start-app.js | 43 ++
.../resources/ui/hive-web/tests/img/spinner.gif | Bin 0 -> 3289 bytes
.../main/resources/ui/hive-web/tests/index.html | 64 +++
.../hive-web/tests/integration/database-test.js | 105 ++++
.../hive-web/tests/integration/history-test.js | 95 ++++
.../tests/integration/query-editor-test.js | 108 +++++
.../tests/integration/saved-queries-test.js | 126 +++++
.../ui/hive-web/tests/integration/udfs-test.js | 91 ++++
.../resources/ui/hive-web/tests/test-helper.js | 30 ++
.../resources/ui/hive-web/tests/unit/.gitkeep | 0
.../hive-web/tests/unit/adapters/application.js | 48 ++
.../ui/hive-web/tests/unit/adapters/file.js | 39 ++
.../components/alert-message-widget-test.js | 91 ++++
.../unit/components/collapsible-widget-test.js | 44 ++
.../components/column-filter-widget-test.js | 138 ++++++
.../unit/components/date-range-widget-test.js | 130 +++++
.../unit/components/expander-widget-test.js | 58 +++
.../unit/components/extended-input-test.js | 79 +++
.../tests/unit/components/no-bubbling-test.js | 43 ++
.../unit/components/number-range-widget-test.js | 72 +++
.../unit/components/popover-widget-test.js | 34 ++
.../unit/components/progress-widget-test.js | 45 ++
.../tests/unit/components/query-editor-test.js | 50 ++
.../tests/unit/components/select-widget-test.js | 158 ++++++
.../tests/unit/components/tabs-wiget-test.js | 117 +++++
.../unit/components/typeahead-widget-test.js | 45 ++
.../hive-web/tests/unit/controllers/columns.js | 32 ++
.../tests/unit/controllers/databases-test.js | 214 +++++++++
.../tests/unit/controllers/history-test.js | 117 +++++
.../tests/unit/controllers/index-test.js | 179 +++++++
.../tests/unit/controllers/insert-udfs-test.js | 68 +++
.../hive-web/tests/unit/controllers/job-test.js | 62 +++
.../tests/unit/controllers/open-queries-test.js | 103 ++++
.../tests/unit/controllers/queries-test.js | 47 ++
.../hive-web/tests/unit/controllers/tables.js | 32 ++
.../hive-web/tests/unit/controllers/udf-test.js | 94 ++++
.../tests/unit/controllers/udfs-test.js | 62 +++
.../tests/unit/helpers/path-binding-test.js | 35 ++
.../main/resources/ui/hive-web/vendor/.gitkeep | 0
.../vendor/codemirror/codemirror-min.js | 17 +
.../hive-web/vendor/codemirror/codemirror.css | 309 ++++++++++++
.../ui/hive-web/vendor/codemirror/show-hint.css | 38 ++
.../ui/hive-web/vendor/codemirror/show-hint.js | 389 +++++++++++++++
.../ui/hive-web/vendor/codemirror/sql-hint.js | 192 ++++++++
contrib/views/hive/src/main/resources/view.xml | 160 +++++++
.../apache/ambari/view/hive/BaseHiveTest.java | 103 ++++
.../org/apache/ambari/view/hive/HDFSTest.java | 64 +++
.../ambari/view/hive/ServiceTestUtils.java | 63 +++
.../BackgroundJobControllerTest.java | 77 +++
.../hive/resources/files/FileServiceTest.java | 214 +++++++++
.../hive/resources/jobs/JobServiceTest.java | 210 ++++++++
.../view/hive/resources/jobs/LogParserTest.java | 73 +++
.../resources/FileResourceServiceTest.java | 119 +++++
.../savedQueries/SavedQueryServiceTest.java | 177 +++++++
.../hive/resources/udfs/UDFServiceTest.java | 119 +++++
.../ambari/view/hive/utils/HdfsApiMock.java | 84 ++++
.../utils/SeekableByteArrayInputStream.java | 71 +++
contrib/views/pom.xml | 1 +
pom.xml | 1 +
274 files changed, 21147 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/pom.xml
----------------------------------------------------------------------
diff --git a/contrib/views/hive/pom.xml b/contrib/views/hive/pom.xml
new file mode 100644
index 0000000..0ed6af4
--- /dev/null
+++ b/contrib/views/hive/pom.xml
@@ -0,0 +1,306 @@
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.ambari.contrib.views</groupId>
+ <artifactId>hive</artifactId>
+ <version>0.1.0-SNAPSHOT</version>
+ <name>Hive</name>
+
+ <parent>
+ <groupId>org.apache.ambari.contrib.views</groupId>
+ <artifactId>ambari-contrib-views</artifactId>
+ <version>2.0.0-SNAPSHOT</version>
+ </parent>
+
+ <dependencies>
+ <dependency>
+ <groupId>com.google.inject</groupId>
+ <artifactId>guice</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey.contribs</groupId>
+ <artifactId>jersey-multipart</artifactId>
+ <version>1.18</version>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-client</artifactId>
+ <version>1.8</version>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-core</artifactId>
+ <version>1.18.1</version>
+ </dependency>
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-json</artifactId>
+ <version>1.9</version>
+ </dependency>
+ <dependency>
+ <groupId>com.googlecode.json-simple</groupId>
+ <artifactId>json-simple</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>commons-configuration</groupId>
+ <artifactId>commons-configuration</artifactId>
+ <version>1.6</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-csv</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-collections4</artifactId>
+ <version>4.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ambari</groupId>
+ <artifactId>ambari-views</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.2.2</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <version>3.0.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.5</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-hdfs</artifactId>
+ <version>${hadoop-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-common</artifactId>
+ <version>${hadoop-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-mapreduce-client-common</artifactId>
+ <version>${hadoop-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-mapreduce-client-core</artifactId>
+ <version>${hadoop-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-yarn-client</artifactId>
+ <version>${hadoop-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.ws.rs</groupId>
+ <artifactId>javax.ws.rs-api</artifactId>
+ <version>2.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hive</groupId>
+ <artifactId>hive-service</artifactId>
+ <version>${hive-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hive</groupId>
+ <artifactId>hive-common</artifactId>
+ <version>${hive-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hive</groupId>
+ <artifactId>hive-shims</artifactId>
+ <version>${hive-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hive</groupId>
+ <artifactId>hive-metastore</artifactId>
+ <version>${hive-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hive</groupId>
+ <artifactId>hive-exec</artifactId>
+ <version>${hive-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-cli</groupId>
+ <artifactId>commons-cli</artifactId>
+ <version>1.2</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-lang</groupId>
+ <artifactId>commons-lang</artifactId>
+ <version>2.2</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.thrift</groupId>
+ <artifactId>libthrift</artifactId>
+ <version>0.9.0</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-minicluster</artifactId>
+ <version>${hadoop-version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <properties>
+ <hadoop-version>2.6.0</hadoop-version>
+ <ambari.dir>${project.parent.parent.parent.basedir}</ambari.dir>
+ <hive-version>1.0.0</hive-version>
+ <ambari.version>1.3.0-SNAPSHOT</ambari.version>
+ </properties>
+ <build>
+ <plugins>
+
+ <!-- Building frontend -->
+ <plugin>
+ <groupId>com.github.eirslett</groupId>
+ <artifactId>frontend-maven-plugin</artifactId>
+ <version>0.0.14</version>
+ <configuration>
+ <nodeVersion>v0.10.26</nodeVersion>
+ <npmVersion>1.4.3</npmVersion>
+ <workingDirectory>src/main/resources/ui/hive-web/</workingDirectory>
+ </configuration>
+ <executions>
+ <execution>
+ <id>install node and npm</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>install-node-and-npm</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>npm install</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>npm</goal>
+ </goals>
+ <configuration>
+ <arguments>install --python="${project.basedir}/../src/main/unix/ambari-python-wrap" --unsafe-perm</arguments>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <artifactId>exec-maven-plugin</artifactId>
+ <groupId>org.codehaus.mojo</groupId>
+ <version>1.3.2</version>
+ <executions>
+ <execution>
+ <id>Hive build</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <workingDirectory>${basedir}/src/main/resources/ui/hive-web</workingDirectory>
+ <executable>node/node</executable>
+ <arguments>
+ <argument>node_modules/.bin/ember</argument>
+ <argument>build</argument>
+ </arguments>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.1</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>copy-dependencies</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/lib</outputDirectory>
+ <includeScope>runtime</includeScope>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.vafer</groupId>
+ <artifactId>jdeb</artifactId>
+ <version>1.0.1</version>
+ <executions>
+ <execution>
+ <phase>none</phase>
+ <goals>
+ <goal>jdeb</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <submodules>false</submodules>
+ </configuration>
+ </plugin>
+ </plugins>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>false</filtering>
+ <includes>
+ <include>META-INF/**/*</include>
+ <include>view.xml</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>src/main/resources/ui/hive-web/dist</directory>
+ <filtering>false</filtering>
+ </resource>
+ <resource>
+ <targetPath>WEB-INF/lib</targetPath>
+ <filtering>false</filtering>
+ <directory>target/lib</directory>
+ </resource>
+ </resources>
+ </build>
+</project>
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/BaseService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/BaseService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/BaseService.java
new file mode 100644
index 0000000..e28193d
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/BaseService.java
@@ -0,0 +1,58 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive;
+
+import com.google.inject.Inject;
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.persistence.Storage;
+import org.apache.ambari.view.hive.persistence.utils.StorageUtil;
+import org.apache.ambari.view.hive.utils.HdfsApi;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+/**
+ * Parent service
+ */
+public class BaseService {
+ @Inject
+ protected ViewContext context;
+
+ protected final static Logger LOG =
+ LoggerFactory.getLogger(BaseService.class);
+
+ private Storage storage;
+ protected Storage getStorage() {
+ if (storage == null) {
+ storage = StorageUtil.getInstance(context).getStorage();
+ }
+ return storage;
+ }
+
+ private HdfsApi hdfsApi = null;
+ protected HdfsApi getHdfsApi() {
+ if (hdfsApi == null)
+ hdfsApi = HdfsApi.getInstance(context);
+ return hdfsApi;
+ }
+
+ public BaseService() {
+// Thread.currentThread().setContextClassLoader(null);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/HelpService.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/HelpService.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/HelpService.java
new file mode 100644
index 0000000..f975393
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/HelpService.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive;
+
+import com.google.inject.Inject;
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.ViewResourceHandler;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * Help service
+ */
+public class HelpService extends BaseService {
+ @Inject
+ ViewContext context;
+
+ @Inject
+ protected ViewResourceHandler handler;
+
+ /**
+ * Constructor
+ */
+ public HelpService() {
+ super();
+ }
+
+ /**
+ * Version
+ * @return version
+ */
+ @GET
+ @Path("/version")
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response version(){
+ return Response.ok("0.0.1-SNAPSHOT").build();
+ }
+
+ /**
+ * Version
+ * @return version
+ */
+ @GET
+ @Path("/test")
+ @Produces(MediaType.TEXT_PLAIN)
+ public Response testStorage(){
+ TestBean test = new TestBean();
+ test.someData = "hello world";
+ getStorage().store(TestBean.class, test);
+ return Response.ok("OK").build();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/TestBean.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/TestBean.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/TestBean.java
new file mode 100644
index 0000000..fce0177
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/TestBean.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive;
+
+import org.apache.ambari.view.hive.persistence.utils.Indexed;
+
+public class TestBean implements Indexed {
+ public String someData;
+ public Integer id;
+
+ @Override
+ public Integer getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(Integer id) {
+ this.id = id;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/backgroundjobs/BackgroundJobController.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/backgroundjobs/BackgroundJobController.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/backgroundjobs/BackgroundJobController.java
new file mode 100644
index 0000000..6bc284e
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/backgroundjobs/BackgroundJobController.java
@@ -0,0 +1,80 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.backgroundjobs;
+
+import org.apache.ambari.view.ViewContext;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class BackgroundJobController {
+ private ViewContext context;
+
+ protected BackgroundJobController(ViewContext context) {
+ this.context = context;
+ }
+
+ private static Map<String, BackgroundJobController> viewSingletonObjects = new HashMap<String, BackgroundJobController>();
+ public static BackgroundJobController getInstance(ViewContext context) {
+ if (!viewSingletonObjects.containsKey(context.getInstanceName()))
+ viewSingletonObjects.put(context.getInstanceName(), new BackgroundJobController(context));
+ return viewSingletonObjects.get(context.getInstanceName());
+ }
+
+ private Map<String, Thread> jobs = new HashMap<String, Thread>();
+ public void startJob(String key, Runnable runnable) {
+ if (jobs.containsKey(key)) {
+ interrupt(key);
+ try {
+ jobs.get(key).join();
+ } catch (InterruptedException ignored) {
+ }
+ }
+ Thread t = new Thread(runnable);
+ jobs.put(key, t);
+ t.start();
+ }
+
+ public Thread.State state(String key) {
+ if (!jobs.containsKey(key))
+ return Thread.State.TERMINATED;
+
+ Thread.State state = jobs.get(key).getState();
+
+ if (state == Thread.State.TERMINATED)
+ jobs.remove(key);
+
+ return state;
+ }
+
+ public boolean interrupt(String key) {
+ if (!jobs.containsKey(key))
+ return false;
+
+ jobs.get(key).interrupt();
+ return true;
+ }
+
+ public boolean isInterrupted(String key) {
+ if (state(key) == Thread.State.TERMINATED)
+ return true;
+
+ return jobs.get(key).isInterrupted();
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ColumnDescription.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ColumnDescription.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ColumnDescription.java
new file mode 100644
index 0000000..d7ea560
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ColumnDescription.java
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+public interface ColumnDescription {
+ public abstract String getName();
+ public abstract void setName(String name);
+
+ public abstract String getType();
+ public abstract void setType(String type);
+
+ public abstract int getPosition();
+ public abstract void setPosition(int position);
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ColumnDescriptionExtended.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ColumnDescriptionExtended.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ColumnDescriptionExtended.java
new file mode 100644
index 0000000..ab54e61
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ColumnDescriptionExtended.java
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+public class ColumnDescriptionExtended implements ColumnDescription {
+ private String name;
+ private String type;
+ private int position;
+ private String comment;
+ private boolean partitioned;
+ private boolean sortedBy;
+ private boolean clusteredBy;
+
+ private ColumnDescriptionExtended(String name, String type, String comment, boolean partitioned,
+ boolean sortedBy, boolean clusteredBy, int position) {
+ setName(name);
+ setType(type);
+ setPosition(position);
+ setComment(comment);
+ setPartitioned(partitioned);
+ setSortedBy(sortedBy);
+ setClusteredBy(clusteredBy);
+ }
+
+ public static ColumnDescription createExtendedColumnDescription(String name, String type, String comment,
+ boolean partitioned, boolean sortedBy, boolean clusteredBy,
+ int position) {
+ return new ColumnDescriptionExtended(name, type, comment, partitioned, sortedBy, clusteredBy, position);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public void setPosition(int position) {
+ this.position = position;
+ }
+
+ public String getComment() {
+ return comment;
+ }
+
+ public void setComment(String comment) {
+ this.comment = comment;
+ }
+
+ public boolean isPartitioned() {
+ return partitioned;
+ }
+
+ public void setPartitioned(boolean partitioned) {
+ this.partitioned = partitioned;
+ }
+
+ public boolean isSortedBy() {
+ return sortedBy;
+ }
+
+ public void setSortedBy(boolean sortedBy) {
+ this.sortedBy = sortedBy;
+ }
+
+ public boolean isClusteredBy() {
+ return clusteredBy;
+ }
+
+ public void setClusteredBy(boolean clusteredBy) {
+ this.clusteredBy = clusteredBy;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ColumnDescriptionShort.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ColumnDescriptionShort.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ColumnDescriptionShort.java
new file mode 100644
index 0000000..a6500aa
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ColumnDescriptionShort.java
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+import java.util.ArrayList;
+
+public class ColumnDescriptionShort extends ArrayList<Object> implements ColumnDescription {
+ public static final int INITIAL_CAPACITY = 3;
+ public static final int NAME_INDEX = 0;
+ public static final int TYPE_INDEX = 1;
+ public static final int POSITION_INDEX = 2;
+
+ private ColumnDescriptionShort(String name, String type, int position) {
+ super(INITIAL_CAPACITY);
+ this.add(null);
+ this.add(null);
+ this.add(null);
+ setName(name);
+ setType(type);
+ setPosition(position);
+ }
+
+ public static ColumnDescription createShortColumnDescription(String name, String type, int position) {
+ return new ColumnDescriptionShort(name, type, position);
+ }
+
+ @Override
+ public String getName() {
+ return (String) this.get(NAME_INDEX);
+ }
+
+ @Override
+ public void setName(String name) {
+ this.set(NAME_INDEX, name);
+ }
+
+ @Override
+ public String getType() {
+ return (String) this.get(TYPE_INDEX);
+ }
+
+ @Override
+ public void setType(String type) {
+ this.set(TYPE_INDEX, type);
+ }
+
+ @Override
+ public int getPosition() {
+ return (Integer) this.get(POSITION_INDEX);
+ }
+
+ @Override
+ public void setPosition(int position) {
+ this.set(POSITION_INDEX, position);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java
new file mode 100644
index 0000000..e713aba
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Connection.java
@@ -0,0 +1,401 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+import org.apache.hadoop.hive.shims.ShimLoader;
+import org.apache.hive.service.auth.HiveAuthFactory;
+import org.apache.hive.service.auth.KerberosSaslHelper;
+import org.apache.hive.service.auth.PlainSaslHelper;
+import org.apache.hive.service.auth.SaslQOP;
+import org.apache.hive.service.cli.thrift.*;
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.transport.TTransport;
+import org.apache.thrift.transport.TTransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Holds session
+ */
+public class Connection {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(Connection.class);
+ private String host;
+ private int port;
+ private Map<String, String> authParams;
+
+ private TCLIService.Client client = null;
+ private TSessionHandle sessHandle = null;
+ private TProtocolVersion protocol = null;
+ private TTransport transport;
+
+ private DDLDelegator ddl;
+
+ public Connection(String host, int port, Map<String, String> authParams) throws HiveClientException {
+ this.host = host;
+ this.port = port;
+ this.authParams = authParams;
+
+ openConnection();
+ ddl = new DDLDelegator(this);
+ }
+
+ public DDLDelegator ddl() {
+ return ddl;
+ }
+
+ public synchronized void openConnection() throws HiveClientException {
+ try {
+ transport = getTransport();
+ transport.open();
+ client = new TCLIService.Client(new TBinaryProtocol(transport));
+ } catch (TTransportException e) {
+ throw new HiveClientException("Could not establish connecton to "
+ + host + ":" + port + ": " + e.toString(), e);
+ }
+ LOG.info("Hive connection opened");
+ openSession();
+ }
+
+ /**
+ * Based on JDBC implementation of HiveConnection.createBinaryTransport
+ * @return transport
+ * @throws HiveClientException
+ */
+ protected TTransport getTransport() throws HiveClientException, TTransportException {
+ TTransport transport;
+ boolean assumeSubject =
+ Utils.HiveAuthenticationParams.AUTH_KERBEROS_AUTH_TYPE_FROM_SUBJECT.equals(authParams
+ .get(Utils.HiveAuthenticationParams.AUTH_KERBEROS_AUTH_TYPE));
+ try {
+ if (!Utils.HiveAuthenticationParams.AUTH_SIMPLE.equalsIgnoreCase(authParams.get(Utils.HiveAuthenticationParams.AUTH_TYPE))) {
+ // If Kerberos
+ Map<String, String> saslProps = new HashMap<String, String>();
+ SaslQOP saslQOP = SaslQOP.AUTH;
+ if (authParams.containsKey(Utils.HiveAuthenticationParams.AUTH_PRINCIPAL)) {
+ if (authParams.containsKey(Utils.HiveAuthenticationParams.AUTH_QOP)) {
+ try {
+ saslQOP = SaslQOP.fromString(authParams.get(Utils.HiveAuthenticationParams.AUTH_QOP));
+ } catch (IllegalArgumentException e) {
+ throw new HiveClientException("Invalid " + Utils.HiveAuthenticationParams.AUTH_QOP +
+ " parameter. " + e.getMessage(), e);
+ }
+ }
+ saslProps.put(Sasl.QOP, saslQOP.toString());
+ saslProps.put(Sasl.SERVER_AUTH, "true");
+ transport = KerberosSaslHelper.getKerberosTransport(
+ authParams.get(Utils.HiveAuthenticationParams.AUTH_PRINCIPAL), host,
+ HiveAuthFactory.getSocketTransport(host, port, 10000), saslProps,
+ assumeSubject);
+ } else {
+ // If there's a delegation token available then use token based connection
+ String tokenStr = getClientDelegationToken(authParams);
+ if (tokenStr != null) {
+ transport = KerberosSaslHelper.getTokenTransport(tokenStr,
+ host, HiveAuthFactory.getSocketTransport(host, port, 10000), saslProps);
+ } else {
+ // we are using PLAIN Sasl connection with user/password
+ String userName = getAuthParamDefault(Utils.HiveAuthenticationParams.AUTH_USER, Utils.HiveAuthenticationParams.ANONYMOUS_USER);
+ String passwd = getAuthParamDefault(Utils.HiveAuthenticationParams.AUTH_PASSWD, Utils.HiveAuthenticationParams.ANONYMOUS_USER);
+ // Note: Thrift returns an SSL socket that is already bound to the specified host:port
+ // Therefore an open called on this would be a no-op later
+ // Hence, any TTransportException related to connecting with the peer are thrown here.
+ // Bubbling them up the call hierarchy so that a retry can happen in openTransport,
+ // if dynamic service discovery is configured.
+ if (isSslConnection()) {
+ // get SSL socket
+ String sslTrustStore = authParams.get(Utils.HiveAuthenticationParams.SSL_TRUST_STORE);
+ String sslTrustStorePassword = authParams.get(Utils.HiveAuthenticationParams.SSL_TRUST_STORE_PASSWORD);
+ if (sslTrustStore == null || sslTrustStore.isEmpty()) {
+ transport = HiveAuthFactory.getSSLSocket(host, port, 10000);
+ } else {
+ transport = HiveAuthFactory.getSSLSocket(host, port, 10000,
+ sslTrustStore, sslTrustStorePassword);
+ }
+ } else {
+ // get non-SSL socket transport
+ transport = HiveAuthFactory.getSocketTransport(host, port, 10000);
+ }
+ // Overlay the SASL transport on top of the base socket transport (SSL or non-SSL)
+ transport = PlainSaslHelper.getPlainTransport(userName, passwd, transport);
+ }
+ }
+ } else {
+ //NOSASL
+ return HiveAuthFactory.getSocketTransport(host, port, 10000);
+ }
+ } catch (SaslException e) {
+ throw new HiveClientException("Could not create secure connection to "
+ + host + ": " + e.getMessage(), e);
+ }
+ return transport;
+ }
+
+ private boolean isSslConnection() {
+ return "true".equalsIgnoreCase(authParams.get(Utils.HiveAuthenticationParams.USE_SSL));
+ }
+
+ // Lookup the delegation token. First in the connection URL, then Configuration
+ private String getClientDelegationToken(Map<String, String> jdbcConnConf) throws HiveClientException {
+ String tokenStr = null;
+ if (Utils.HiveAuthenticationParams.AUTH_TOKEN.equalsIgnoreCase(jdbcConnConf.get(Utils.HiveAuthenticationParams.AUTH_TYPE))) {
+ // check delegation token in job conf if any
+ try {
+ tokenStr = ShimLoader.getHadoopShims().
+ getTokenStrForm(HiveAuthFactory.HS2_CLIENT_TOKEN);
+ } catch (IOException e) {
+ throw new HiveClientException("Error reading token ", e);
+ }
+ }
+ return tokenStr;
+ }
+
+ private String getAuthParamDefault(String key, String defaultValue) {
+ if (authParams.containsKey(key))
+ return authParams.get(key);
+ return defaultValue;
+ }
+
+ private synchronized void openSession() throws HiveClientException {
+ //It's possible to set proxy user configuration here
+ TOpenSessionResp openResp = new HiveCall<TOpenSessionResp>(this) {
+ @Override
+ public TOpenSessionResp body() throws HiveClientException {
+ TOpenSessionReq openReq = new TOpenSessionReq();
+ try {
+ return client.OpenSession(openReq);
+ } catch (TException e) {
+ throw new HiveClientException("Unable to open Hive session", e);
+ }
+
+ }
+ }.call();
+ Utils.verifySuccess(openResp.getStatus(), "Unable to open Hive session");
+
+ protocol = openResp.getServerProtocolVersion();
+ sessHandle = openResp.getSessionHandle();
+ LOG.info("Hive session opened");
+ }
+
+ private synchronized void closeSession() throws HiveClientException {
+ if (sessHandle == null) return;
+ TCloseSessionReq closeReq = new TCloseSessionReq(sessHandle);
+ //It's possible to set proxy user configuration here
+ TCloseSessionResp closeResp = null;
+ try {
+ closeResp = client.CloseSession(closeReq);
+ Utils.verifySuccess(closeResp.getStatus(), "Unable to close Hive session");
+ } catch (TException e) {
+ throw new HiveClientException("Unable to close Hive session", e);
+ }
+
+ sessHandle = null;
+ protocol = null;
+ LOG.info("Hive session closed");
+ }
+
+ public synchronized void closeConnection() throws HiveClientException {
+ if (client == null) return;
+ try {
+ closeSession();
+ } catch (HiveClientException e) {
+ LOG.error("Unable to close Hive session: " + e.getMessage());
+ } finally {
+ transport.close();
+ transport = null;
+ client = null;
+ sessHandle = null;
+ protocol = null;
+ }
+ LOG.info("Connection to Hive closed");
+ }
+
+ /**
+ * Execute query
+ * @param cmd query
+ * @param async wait till query finish?
+ * @return handle of operation
+ * @throws HiveClientException
+ */
+ public TOperationHandle execute(final String cmd, final boolean async) throws HiveClientException {
+ TOperationHandle handle = null;
+ for(final String oneCmd : cmd.split(";")) {
+
+ TExecuteStatementResp execResp = new HiveCall<TExecuteStatementResp>(this) {
+ @Override
+ public TExecuteStatementResp body() throws HiveClientException {
+
+ TExecuteStatementReq execReq = null;
+ execReq = new TExecuteStatementReq(getSessHandle(), oneCmd);
+ execReq.setRunAsync(async);
+ execReq.setConfOverlay(new HashMap<String, String>()); //maybe it's hive configuration? use it, Luke!
+ try {
+ return client.ExecuteStatement(execReq);
+ } catch (TException e) {
+ throw new HiveClientException("Unable to submit statement " + cmd, e);
+ }
+
+ }
+ }.call();
+
+ Utils.verifySuccess(execResp.getStatus(), "Unable to submit statement " + cmd);
+ //TODO: check if status have results
+ handle = execResp.getOperationHandle();
+ }
+ if (handle == null) {
+ throw new HiveClientException("Empty command given", null);
+ }
+ return handle;
+ }
+
+ public TOperationHandle executeAsync(String cmd) throws HiveClientException {
+ return execute(cmd, true);
+ }
+
+ public TOperationHandle executeSync(String cmd) throws HiveClientException {
+ return execute(cmd, false);
+ }
+
+ public String getLogs(TOperationHandle handle) {
+ LogsCursor results = new LogsCursor(this, handle);
+ results.reset(); // we have to read from FIRST line, to get
+ // logs from beginning on every call this function
+ List<String> logLineList = results.getValuesInColumn(0);
+ StringBuilder log = new StringBuilder();
+ for(String line : logLineList) {
+ log.append(line);
+ log.append('\n');
+ }
+ return log.toString();
+ }
+
+ public Cursor getResults(TOperationHandle handle) {
+ Cursor cursor = new Cursor(this, handle);
+ cursor.reset(); // we have to read from FIRST line, to get
+ // logs from beginning on every call this function
+ return cursor;
+ }
+
+ /**
+ * Retrieve status of operation
+ * @param operationHandle handle
+ * @return thrift status response object
+ * @throws HiveClientException
+ */
+ public TGetOperationStatusResp getOperationStatus(final TOperationHandle operationHandle) throws HiveClientException {
+ return new HiveCall<TGetOperationStatusResp>(this) {
+ @Override
+ public TGetOperationStatusResp body() throws HiveClientException {
+
+ TGetOperationStatusReq statusReq = new TGetOperationStatusReq(operationHandle);
+ try {
+ return client.GetOperationStatus(statusReq);
+ } catch (TException e) {
+ throw new HiveClientException("Unable to fetch operation status", e);
+ }
+
+ }
+ }.call();
+// transportLock.lock();
+// try {
+// return client.GetOperationStatus(statusReq);
+// } catch (TException e) {
+// throw new HiveClientException("Unable to fetch operation status", e);
+// } finally {
+// transportLock.unlock();
+// }
+ }
+
+ /**
+ * Cancel operation
+ * @param operationHandle operation handle
+ */
+ public void cancelOperation(final TOperationHandle operationHandle) throws HiveClientException {
+ TCancelOperationResp cancelResp = new HiveCall<TCancelOperationResp>(this) {
+ @Override
+ public TCancelOperationResp body() throws HiveClientException {
+ TCancelOperationReq cancelReq = new TCancelOperationReq(operationHandle);
+ try {
+ return client.CancelOperation(cancelReq);
+ } catch (TException e) {
+ throw new HiveClientException("Unable to cancel operation", null);
+ }
+ }
+ }.call();
+ Utils.verifySuccess(cancelResp.getStatus(), "Unable to cancel operation");
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public TSessionHandle getSessHandle() throws HiveClientException {
+ if (sessHandle == null)
+ openSession();
+ return sessHandle;
+ }
+
+ public void setSessHandle(TSessionHandle sessHandle) {
+ this.sessHandle = sessHandle;
+ }
+
+ public TCLIService.Client getClient() {
+ return client;
+ }
+
+ public void setClient(TCLIService.Client client) {
+ this.client = client;
+ }
+
+ public TProtocolVersion getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(TProtocolVersion protocol) {
+ this.protocol = protocol;
+ }
+
+ public Map<String, String> getAuthParams() {
+ return authParams;
+ }
+
+ public void setAuthParams(Map<String, String> authParams) {
+ this.authParams = authParams;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionPool.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionPool.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionPool.java
new file mode 100644
index 0000000..6d07067
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/ConnectionPool.java
@@ -0,0 +1,81 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+import org.apache.ambari.view.ViewContext;
+import org.apache.ambari.view.hive.utils.ServiceFormattedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class ConnectionPool {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(ConnectionPool.class);
+
+ private static Map<String, Connection> viewSingletonObjects = new HashMap<String, Connection>();
+ /**
+ * Returns HdfsApi object specific to instance
+ * @param context View Context instance
+ * @return Hdfs business delegate object
+ */
+ public static Connection getConnection(ViewContext context) {
+ if (!viewSingletonObjects.containsKey(context.getInstanceName()))
+ viewSingletonObjects.put(context.getInstanceName(), connectToHive(context));
+ return viewSingletonObjects.get(context.getInstanceName());
+ }
+
+ private static Connection connectToHive(ViewContext context) {
+ try {
+ return new Connection(getHiveHost(context), Integer.valueOf(getHivePort(context)), getHiveAuthParams(context));
+ } catch (HiveClientException e) {
+ throw new ServiceFormattedException("Couldn't open connection to Hive: " + e.toString(), e);
+ }
+ }
+
+ public static void setInstance(ViewContext context, Connection api) {
+ viewSingletonObjects.put(context.getInstanceName(), api);
+ }
+
+ private static String getHiveHost(ViewContext context) {
+ return context.getProperties().get("hive.host");
+ }
+
+ private static String getHivePort(ViewContext context) {
+ return context.getProperties().get("hive.port");
+ }
+
+ private static Map<String, String> getHiveAuthParams(ViewContext context) {
+ String auth = context.getProperties().get("hive.auth");
+ Map<String, String> params = new HashMap<String, String>();
+ if (auth == null || auth.isEmpty()) {
+ auth = "auth=NOSASL";
+ }
+ for(String param : auth.split(";")) {
+ String[] keyvalue = param.split("=");
+ if (keyvalue.length != 2) {
+ LOG.error("Can not parse authentication param " + param + " in " + auth);
+ continue;
+ }
+ params.put(keyvalue[0], keyvalue[1]);
+ }
+ return params;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java
new file mode 100644
index 0000000..84987f5
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/Cursor.java
@@ -0,0 +1,233 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+import static org.apache.hive.service.cli.thrift.TCLIServiceConstants.TYPE_NAMES;
+
+import org.apache.ambari.view.hive.utils.BadRequestFormattedException;
+import org.apache.hive.service.cli.RowSet;
+import org.apache.hive.service.cli.RowSetFactory;
+import org.apache.hive.service.cli.thrift.*;
+import org.apache.thrift.TException;
+import sun.reflect.generics.reflectiveObjects.NotImplementedException;
+
+import java.util.*;
+
+public class Cursor implements Iterator<Row>, Iterable<Row> {
+ private final int FETCH_SIZE = 50;
+
+ private TCLIService.Client client;
+ private TOperationHandle opHandle;
+
+ private RowSet fetched = null;
+ private Iterator<Object[]> fetchedIterator = null;
+ private Connection connection;
+ private boolean resetCursor = false;
+ private ArrayList<ColumnDescription> schema;
+ private long offset;
+ private HashSet<Integer> selectedColumns = new LinkedHashSet<Integer>();
+
+ public Cursor(Connection connection, TOperationHandle opHandle) {
+ this.connection = connection;
+ this.client = connection.getClient();
+ this.opHandle = opHandle;
+ }
+
+ public TOperationHandle getOpHandle() {
+ return opHandle;
+ }
+
+ public void setOpHandle(TOperationHandle opHandle) {
+ this.opHandle = opHandle;
+ }
+
+ private void fetchNextBlock() throws HiveClientException {
+ //fetch another bunch
+ TFetchResultsResp fetchResp = new HiveCall<TFetchResultsResp>(connection) {
+ @Override
+ public TFetchResultsResp body() throws HiveClientException {
+ TFetchOrientation orientation = TFetchOrientation.FETCH_NEXT;
+ if (resetCursor) {
+ orientation = TFetchOrientation.FETCH_FIRST;
+ resetCursor = false;
+ offset = 0;
+ }
+
+ TFetchResultsReq fetchReq = getFetchResultsReq(orientation);
+ try {
+ return client.FetchResults(fetchReq);
+ } catch (TException e) {
+ throw new HiveClientException("Unable to fetch results", e);
+ }
+
+ }
+ }.call();
+ Utils.verifySuccess(fetchResp.getStatus(), "Unable to fetch results");
+ TRowSet results = fetchResp.getResults();
+ fetched = RowSetFactory.create(results, connection.getProtocol());
+ fetchedIterator = fetched.iterator();
+ }
+
+ protected TFetchResultsReq getFetchResultsReq(TFetchOrientation orientation) {
+ return new TFetchResultsReq(opHandle, orientation, FETCH_SIZE);
+ }
+
+ public ArrayList<ColumnDescription> getSchema() throws HiveClientException {
+ if (this.schema == null) {
+ // TODO: extract all HiveCall inline classes to separate files
+ TGetResultSetMetadataResp fetchResp = new HiveCall<TGetResultSetMetadataResp>(connection) {
+ @Override
+ public TGetResultSetMetadataResp body() throws HiveClientException {
+
+ TGetResultSetMetadataReq fetchReq = new TGetResultSetMetadataReq(opHandle);
+ try {
+ return client.GetResultSetMetadata(fetchReq);
+ } catch (TException e) {
+ throw new HiveClientException("Unable to fetch results metadata", e);
+ }
+
+ }
+ }.call();
+ Utils.verifySuccess(fetchResp.getStatus(), "Unable to fetch results metadata");
+ TTableSchema schema = fetchResp.getSchema();
+
+ List<TColumnDesc> thriftColumns = schema.getColumns();
+ ArrayList<ColumnDescription> columnDescriptions = new ArrayList<ColumnDescription>(thriftColumns.size());
+
+ for (TColumnDesc columnDesc : thriftColumns) {
+ String name = columnDesc.getColumnName();
+ String type = TYPE_NAMES.get(columnDesc.getTypeDesc().getTypes().get(0).getPrimitiveEntry().getType());
+ int position = columnDesc.getPosition();
+ columnDescriptions.add(ColumnDescriptionShort.createShortColumnDescription(name, type, position));
+ }
+ if (selectedColumns.size() == 0)
+ this.schema = columnDescriptions;
+ else {
+ ArrayList<ColumnDescription> selectedColumnsSchema = new ArrayList<ColumnDescription>();
+ for (Integer selectedIndex : selectedColumns) {
+ selectedColumnsSchema.add(columnDescriptions.get(selectedIndex));
+ }
+ this.schema = selectedColumnsSchema;
+ }
+ }
+ return this.schema;
+ }
+
+ /**
+ * Get list with all values in one column
+ * @param column column index
+ * @return list of objects in column
+ */
+ public <T> List<T> getValuesInColumn(int column) {
+ LinkedList<T> list = new LinkedList<T>();
+ for (Row row : this) {
+ list.add((T) row.getRow()[column]);
+ }
+ return list;
+ }
+
+ /**
+ * Get logs Result object
+ * @return Result object configured to fetch logs
+ */
+ public Cursor getLogs() {
+ return new LogsCursor(connection, opHandle);
+ }
+
+ public void reset() {
+ fetchedIterator = null;
+ fetched = null;
+ resetCursor = true;
+ offset = 0;
+ }
+
+ @Override
+ public boolean hasNext() {
+ fetchIfNeeded();
+ return fetchedIterator.hasNext();
+ }
+
+ private void fetchIfNeeded() {
+ if (fetchedIterator == null || !fetchedIterator.hasNext()) {
+ try {
+ fetchNextBlock();
+ } catch (HiveClientException e) {
+ throw new HiveClientRuntimeException(e.getMessage(), e);
+ }
+ }
+ }
+
+ @Override
+ public Row next() {
+ if (!hasNext())
+ throw new NoSuchElementException();
+ Row row = new Row(fetchedIterator.next(), selectedColumns);
+ offset ++;
+ return row;
+ }
+
+ @Override
+ public void remove() {
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public Iterator<Row> iterator() {
+ return this;
+ }
+
+// public int size() {
+// fetchIfNeeded();
+// return fetched.numRows();
+// }
+ public long getOffset() {
+ return offset;
+ }
+
+ public int read(ArrayList<Row> rows, int count) {
+ int read = 0;
+ while(read < count && hasNext()) {
+ rows.add(next());
+ read ++;
+ }
+ return read;
+ }
+
+ public int readRaw(ArrayList<Object[]> rows, int count) {
+ int read = 0;
+ while(read < count && hasNext()) {
+ rows.add(next().getRow());
+ read ++;
+ }
+ return read;
+ }
+
+ public void selectColumns(String columnsRequested) {
+ selectedColumns.clear();
+ if (columnsRequested != null) {
+ for (String columnRequested : columnsRequested.split(",")) {
+ try {
+ selectedColumns.add(Integer.parseInt(columnRequested));
+ } catch (NumberFormatException ex) {
+ throw new BadRequestFormattedException("Columns param should be comma-separated integers", ex);
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java
new file mode 100644
index 0000000..9cb5a1b
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/DDLDelegator.java
@@ -0,0 +1,140 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+import org.apache.hive.service.cli.thrift.*;
+import org.apache.thrift.TException;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class DDLDelegator {
+ private Connection connection;
+
+ public DDLDelegator(Connection connection) {
+ this.connection = connection;
+ }
+
+ /**
+ * Retrieve list of tables in DB
+ * @param db db name
+ * @return list of table names
+ * @throws HiveClientException
+ */
+ public List<String> getTableList(String db, String like) throws HiveClientException {
+ Cursor cursor = getTableListCursor(db, like);
+ return cursor.getValuesInColumn(0);
+ }
+
+ /**
+ * Retrieve list of tables in DB results set
+ * @param db db name
+ * @return list of table names
+ * @throws HiveClientException
+ */
+ public Cursor getTableListCursor(String db, String like) throws HiveClientException {
+ connection.executeSync(String.format("use %s", db));
+ TOperationHandle handle = connection.executeSync(String.format("show tables like '%s'", like));
+
+ return new Cursor(connection, handle);
+ }
+
+ /**
+ * Retrieve databases
+ * @param like '*' for all
+ * @return list of databases
+ * @throws HiveClientException
+ */
+ public List<String> getDBList(String like) throws HiveClientException {
+ Cursor cursor = getDBListCursor(like);
+ return cursor.getValuesInColumn(0);
+ }
+
+ /**
+ * Retrieve databases results set
+ * @param like '*' for all
+ * @return list of databases
+ * @throws HiveClientException
+ */
+ public Cursor getDBListCursor(String like) throws HiveClientException {
+ TOperationHandle handle = connection.executeSync(String.format("show databases like '%s'", like));
+ return new Cursor(connection, handle);
+ }
+
+ /**
+ * Retrieve table schema
+ * @param db database name
+ * @param table table name
+ * @return schema
+ * @throws HiveClientException
+ */
+ public List<ColumnDescription> getTableDescription(final String db, final String table, String like, boolean extended) throws HiveClientException {
+ List<ColumnDescription> columnDescriptions = new LinkedList<ColumnDescription>();
+ Cursor cursor = getTableDescriptionCursor(db, table, like);
+ for(Row row : cursor) {
+ Object[] rowObjects = row.getRow();
+
+ ColumnDescription columnDescription;
+ if (extended) {
+ //TODO: retrieve sortedBy, clusteredBy, partitioned
+ columnDescription = ColumnDescriptionExtended.createExtendedColumnDescription(
+ (String) rowObjects[3], (String) rowObjects[5], (String) rowObjects[11],
+ false, false, false, (Integer) rowObjects[16]);
+ } else {
+ columnDescription = ColumnDescriptionShort.createShortColumnDescription(
+ (String) rowObjects[3], (String) rowObjects[5], (Integer) rowObjects[16]);
+ }
+ columnDescriptions.add(columnDescription);
+ }
+ return columnDescriptions;
+ }
+
+ /**
+ * Retrieve table schema results set
+ * @param db database name
+ * @param table table name
+ * @return schema
+ * @throws HiveClientException
+ */
+ public Cursor getTableDescriptionCursor(final String db, final String table, String like) throws HiveClientException {
+ if (like == null)
+ like = ".*";
+ else
+ like = ".*" + like + ".*";
+ final String finalLike = like;
+ TGetColumnsResp resp = new HiveCall<TGetColumnsResp>(connection) {
+ @Override
+ public TGetColumnsResp body() throws HiveClientException {
+
+ TGetColumnsReq req = new TGetColumnsReq(conn.getSessHandle());
+ req.setSchemaName(db);
+ req.setTableName(table);
+ req.setColumnName(finalLike);
+ try {
+ return connection.getClient().GetColumns(req);
+ } catch (TException e) {
+ throw new HiveClientException("Unable to get table columns", e);
+ }
+ }
+
+ }.call();
+
+ return new Cursor(connection, resp.getOperationHandle());
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveCall.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveCall.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveCall.java
new file mode 100644
index 0000000..c8b43e2
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveCall.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+import org.apache.commons.lang.exception.ExceptionUtils;
+import org.apache.thrift.transport.TTransportException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class HiveCall <T> {
+ private final static Logger LOG =
+ LoggerFactory.getLogger(HiveCall.class);
+
+ protected final Connection conn;
+
+ public HiveCall(Connection connection) {
+ this.conn = connection;
+ }
+
+ public abstract T body() throws HiveClientException;
+
+ public T call() throws HiveClientException {
+ T result = null;
+ boolean needRetry = false;
+ int attempts = 0;
+ do {
+ if (needRetry) {
+ needRetry = false;
+ attempts += 1;
+ conn.closeConnection();
+ conn.openConnection();
+ }
+ try {
+
+ synchronized (conn) {
+ result = body();
+ }
+
+ } catch (HiveClientException ex) {
+ Throwable root = ExceptionUtils.getRootCause(ex);
+ if (attempts < 2 && root instanceof TTransportException) {
+ needRetry = true;
+ LOG.error("Retry call because of Transport Exception: " + root.toString());
+ continue;
+ }
+ throw ex;
+ }
+ } while (needRetry);
+ return result;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveClientException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveClientException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveClientException.java
new file mode 100644
index 0000000..9dd04de
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveClientException.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+public class HiveClientException extends Exception {
+ public HiveClientException(String comment, Exception ex) {
+ super(comment + ((ex == null)?"":(": " + ex.toString())), ex);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveClientRuntimeException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveClientRuntimeException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveClientRuntimeException.java
new file mode 100644
index 0000000..1393012
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveClientRuntimeException.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+public class HiveClientRuntimeException extends RuntimeException {
+ public HiveClientRuntimeException(String comment, Exception ex) {
+ super(comment + ((ex == null)?"":(": " + ex.toString())), ex);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java
new file mode 100644
index 0000000..7adbe23
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/HiveErrorStatusException.java
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+import org.apache.hive.service.cli.thrift.TStatusCode;
+
+/**
+ * Some thrift operation done with status 'failed'
+ */
+public class HiveErrorStatusException extends HiveClientException {
+ public HiveErrorStatusException(TStatusCode statusCode, String comment) {
+ super(String.format("Failed with status %s: %s", statusCode, comment), null);
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/f5482d89/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/LogsCursor.java
----------------------------------------------------------------------
diff --git a/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/LogsCursor.java b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/LogsCursor.java
new file mode 100644
index 0000000..a6705e4
--- /dev/null
+++ b/contrib/views/hive/src/main/java/org/apache/ambari/view/hive/client/LogsCursor.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.view.hive.client;
+
+import org.apache.hive.service.cli.thrift.TFetchOrientation;
+import org.apache.hive.service.cli.thrift.TFetchResultsReq;
+import org.apache.hive.service.cli.thrift.TOperationHandle;
+
+public class LogsCursor extends Cursor {
+ public LogsCursor(Connection connection, TOperationHandle opHandle) {
+ super(connection, opHandle);
+ }
+
+ @Override
+ protected TFetchResultsReq getFetchResultsReq(TFetchOrientation orientation) {
+ TFetchResultsReq req = super.getFetchResultsReq(orientation);
+ req.setFetchType((short) 1);
+ return req;
+ }
+}