You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by bi...@apache.org on 2019/03/29 20:55:19 UTC
[hadoop] 01/02: YARN-7129. Application Catalog for YARN
applications. Contributed by Eric Yang
This is an automated email from the ASF dual-hosted git repository.
billie pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/hadoop.git
commit 7ee32a2eb187d43f1c8d7c6416eebc13d461ee1e
Author: Billie Rinaldi <bi...@apache.org>
AuthorDate: Fri Mar 29 07:42:58 2019 -0700
YARN-7129. Application Catalog for YARN applications. Contributed by Eric Yang
---
hadoop-project/pom.xml | 48 +-
.../hadoop-yarn-applications-catalog/README.md | 50 +
.../Dockerfile | 37 +
.../README.md | 43 +
.../pom.xml | 108 ++
.../src/main/resources/jaas.config | 9 +
.../src/main/resources/samples.xml | 135 ++
.../src/main/scripts/entrypoint.sh | 45 +
.../src/main/scripts/setup-image.sh | 21 +
.../.gitignore | 3 +
.../.yarnrc | 1 +
.../README.md | 51 +
.../dev-support/findbugs-exclude.xml | 20 +
.../package.json | 40 +
.../pom.xml | 455 ++++++
.../yarn/appcatalog/application/AppCatalog.java | 60 +
.../application/AppCatalogSolrClient.java | 359 +++++
.../appcatalog/application/YarnServiceClient.java | 174 +++
.../yarn/appcatalog/application/package-info.java | 22 +
.../controller/AppDetailsController.java | 265 ++++
.../appcatalog/controller/AppListController.java | 182 +++
.../appcatalog/controller/AppStoreController.java | 198 +++
.../yarn/appcatalog/controller/package-info.java | 22 +
.../hadoop/yarn/appcatalog/model/AppDetails.java | 76 +
.../hadoop/yarn/appcatalog/model/AppEntry.java | 72 +
.../yarn/appcatalog/model/AppStoreEntry.java | 106 ++
.../hadoop/yarn/appcatalog/model/Application.java | 67 +
.../hadoop/yarn/appcatalog/model/package-info.java | 22 +
.../hadoop/yarn/appcatalog/utils/RandomWord.java | 422 ++++++
.../yarn/appcatalog/utils/WordLengthException.java | 29 +
.../hadoop/yarn/appcatalog/utils/package-info.java | 22 +
.../src/main/javascript/app.js | 59 +
.../src/main/javascript/controllers.js | 320 +++++
.../src/main/javascript/filters.js | 32 +
.../src/main/javascript/routes.js | 17 +
.../src/main/javascript/services.js | 17 +
.../src/main/resources/appcatalog.properties | 1 +
.../src/main/resources/log4j.properties | 23 +
.../src/main/webapp/.gitignore | 1 +
.../src/main/webapp/WEB-INF/beans.xml | 22 +
.../src/main/webapp/WEB-INF/web.xml | 75 +
.../src/main/webapp/css/bootstrap-hadoop.css | 1488 ++++++++++++++++++++
.../src/main/webapp/css/bootstrap-hadoop.min.css | 18 +
.../src/main/webapp/css/img/feather.png | Bin 0 -> 6066 bytes
.../src/main/webapp/css/img/jenkins.png | Bin 0 -> 23140 bytes
.../src/main/webapp/css/img/loading.svg | 8 +
.../pattern-f61c2e99f82389a67432f54155c5f483.png | Bin 0 -> 27786 bytes
.../src/main/webapp/css/specific.css | 252 ++++
.../src/main/webapp/css/theme.css | 30 +
.../src/main/webapp/index.html | 82 ++
.../src/main/webapp/js/bootstrap-hadoop.js | 284 ++++
.../src/main/webapp/js/bootstrap-hadoop.min.js | 19 +
.../src/main/webapp/partials/deploy.html | 80 ++
.../src/main/webapp/partials/details.html | 64 +
.../src/main/webapp/partials/home.html | 52 +
.../src/main/webapp/partials/new.html | 111 ++
.../src/main/webapp/theme.html | 649 +++++++++
.../application/EmbeddedSolrServerFactory.java | 104 ++
.../application/TestAppCatalogSolrClient.java | 112 ++
.../controller/AppDetailsControllerTest.java | 138 ++
.../controller/AppListControllerTest.java | 95 ++
.../controller/AppStoreControllerTest.java | 97 ++
.../src/test/javascript/controllersSpec.js | 249 ++++
.../src/test/javascript/karma.conf.js | 34 +
.../src/test/resources/configsets.tgz | Bin 0 -> 2996 bytes
.../exampleCollection/conf/lang/stopwords_en.txt | 54 +
.../configsets/exampleCollection/conf/params.json | 20 +
.../exampleCollection/conf/protwords.txt | 20 +
.../configsets/exampleCollection/conf/schema.xml | 115 ++
.../exampleCollection/conf/solrconfig.xml | 36 +
.../exampleCollection/conf/stopwords.txt | 14 +
.../configsets/exampleCollection/conf/synonyms.txt | 28 +
.../src/test/resources/log4j.properties | 11 +
.../hadoop-yarn-applications-catalog/pom.xml | 37 +
.../yarn/service/client/ApiServiceClient.java | 13 +-
.../examples/appcatalog/appcatalog.json | 28 +
.../hadoop-yarn/hadoop-yarn-applications/pom.xml | 1 +
.../src/site/markdown/yarn-service/Examples.md | 13 +
78 files changed, 8083 insertions(+), 4 deletions(-)
diff --git a/hadoop-project/pom.xml b/hadoop-project/pom.xml
index a5af081..7bd012d 100644
--- a/hadoop-project/pom.xml
+++ b/hadoop-project/pom.xml
@@ -147,7 +147,13 @@
<surefire.fork.timeout>900</surefire.fork.timeout>
<aws-java-sdk.version>1.11.375</aws-java-sdk.version>
<hsqldb.version>2.3.4</hsqldb.version>
- <frontend-maven-plugin.version>1.5</frontend-maven-plugin.version>
+ <frontend-maven-plugin.version>1.6</frontend-maven-plugin.version>
+ <maven-dependency-plugin.version>3.0.1</maven-dependency-plugin.version>
+ <jasmine-maven-plugin.version>2.1</jasmine-maven-plugin.version>
+ <phantomjs-maven-plugin.version>0.7</phantomjs-maven-plugin.version>
+ <yuicompressor-maven-plugin.version>1.5.1</yuicompressor-maven-plugin.version>
+ <maven-project-info-reports-plugin.version>2.9</maven-project-info-reports-plugin.version>
+
<!-- the version of Hadoop declared in the version resources; can be overridden
so that Hadoop 3.x can declare itself a 2.x artifact. -->
<declared.hadoop.version>${hadoop.version}</declared.hadoop.version>
@@ -161,6 +167,8 @@
<junit.vintage.version>5.3.1</junit.vintage.version>
<junit.platform.version>1.3.1</junit.platform.version>
<jline.version>3.9.0</jline.version>
+ <powermock.version>1.5.6</powermock.version>
+ <solr.version>7.7.0</solr.version>
</properties>
<dependencyManagement>
@@ -467,6 +475,19 @@
<dependency>
<groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-yarn-services-api</artifactId>
+ <version>${hadoop.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-yarn-applications-catalog-webapp</artifactId>
+ <version>${hadoop.version}</version>
+ <type>war</type>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-jobclient</artifactId>
<version>${hadoop.version}</version>
<type>test-jar</type>
@@ -1651,6 +1672,31 @@
<artifactId>hadoop-maven-plugins</artifactId>
<version>${hadoop.version}</version>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>${maven-dependency-plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>net.alchim31.maven</groupId>
+ <artifactId>yuicompressor-maven-plugin</artifactId>
+ <version>${yuicompressor-maven-plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>com.github.klieber</groupId>
+ <artifactId>phantomjs-maven-plugin</artifactId>
+ <version>${phantomjs-maven-plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>com.github.searls</groupId>
+ <artifactId>jasmine-maven-plugin</artifactId>
+ <version>${jasmine-maven-plugin.version}</version>
+ </plugin>
+ <plugin>
+ <groupId>com.github.eirslett</groupId>
+ <artifactId>frontend-maven-plugin</artifactId>
+ <version>${frontend-maven-plugin.version}</version>
+ </plugin>
</plugins>
</pluginManagement>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/README.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/README.md
new file mode 100644
index 0000000..f048661
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/README.md
@@ -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.
+-->
+# Apache Hadoop YARN Application Catalog
+
+## Introduction
+
+YARN Application Catalog is application catalog for
+deploying docker enabled cloud application on Hadoop.
+
+check it out:
+
+```bash
+git clone https://github.com/apache/hadoop.git
+```
+
+## Prerequisites
+* Firefox or Chrome
+* [npm](https://www.npmjs.org)
+* [nodejs](http://nodejs.org)
+* [JDK](http://www.oracle.com/technetwork/java/javaee/downloads/index.html)
+* [IDE](http://www.jetbrains.com/)
+* [bower](http://bower.io)
+* [PhantomJs](http://phantomjs.org) or `brew install phantomjs`
+* [Docker](http://docker.io)
+
+## Installation
+
+```bash
+mvn package
+```
+
+When running this command a couple of things happen:
+* YARN Application Catalog web application is built
+* YARN Application Docker image is built
+
+## Status of the project
+
+See Apache [JIRA](http://issues.apache.org/jira/browse/HADOOP)
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/Dockerfile b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/Dockerfile
new file mode 100644
index 0000000..4a3ec65
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/Dockerfile
@@ -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.
+
+FROM centos:7
+
+RUN yum -y install tomcat lsof krb5-workstation sssd-client curl
+SHELL ["/bin/bash", "-o", "pipefail", "-c"]
+RUN mkdir -p /opt/apache/solr && \
+ curl -SL http://archive.apache.org/dist/lucene/solr/7.7.0/solr-7.7.0.tgz | \
+ tar -xzC /opt/apache/solr --strip 1
+COPY src/main/scripts/setup-image.sh /setup-image.sh
+COPY src/main/resources/samples.xml /tmp/samples.xml
+COPY src/main/resources/jaas.config /etc/tomcat/jaas.config.template
+COPY src/main/scripts/entrypoint.sh /usr/bin/entrypoint.sh
+COPY target/ROOT.war /var/lib/tomcat/webapps/ROOT.war
+RUN chmod 755 /setup-image.sh
+RUN chmod 755 /usr/bin/entrypoint.sh
+RUN /setup-image.sh
+
+EXPOSE 8080
+EXPOSE 8983
+WORKDIR /
+
+ENTRYPOINT ["/usr/bin/entrypoint.sh" ]
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/README.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/README.md
new file mode 100644
index 0000000..1e2d12b
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/README.md
@@ -0,0 +1,43 @@
+<!---
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. See accompanying LICENSE file.
+-->
+# Apache Hadoop YARN AppCatalog Docker Image
+
+## Introduction
+
+AppCatalog Docker image is pre-packaged docker container for Hadoop Application Catalog.
+
+check it out:
+
+```
+git clone https://github.com/apache/hadoop.git
+```
+
+## Compile
+
+```
+mvn package
+```
+
+## Run
+
+```
+docker run -d -p 8080:8080 -p 8983:8983 hadoop/appcatalog-docker:1.0-SNAPSHOT
+```
+
+When running this command a couple of things happens:
+* Solr server will create appcatalog collection for hosting application catalog
+* Sample applications are registered in Embedded Solr
+* Tomcat will run appcatalog-webapp on port 8080
+
+User can browse port 8080 to deploy application on a Hadoop cluster.
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/pom.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/pom.xml
new file mode 100644
index 0000000..7c0d3ef
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/pom.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<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/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-yarn-applications-catalog-docker</artifactId>
+ <packaging>pom</packaging>
+
+ <parent>
+ <artifactId>hadoop-yarn-applications-catalog</artifactId>
+ <groupId>org.apache.hadoop</groupId>
+ <version>3.3.0-SNAPSHOT</version>
+ </parent>
+
+ <name>YARN Application Catalog Docker Image</name>
+ <url>http://maven.apache.org</url>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-yarn-applications-catalog-webapp</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>src/main/resources/jaas.config</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>docker</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>3.0.1</version>
+ <executions>
+ <execution>
+ <id>copy-dependencies</id>
+ <phase>package</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-yarn-applications-catalog-webapp</artifactId>
+ <version>${project.version}</version>
+ <destFileName>ROOT.war</destFileName>
+ <type>war</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${project.build.directory}</outputDirectory>
+ <overWriteReleases>false</overWriteReleases>
+ <overWriteSnapshots>true</overWriteSnapshots>
+ <excludeTransitive>true</excludeTransitive>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>com.spotify</groupId>
+ <artifactId>dockerfile-maven-plugin</artifactId>
+ <version>1.3.4</version>
+ <executions>
+ <execution>
+ <id>default</id>
+ <goals>
+ <goal>build</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <repository>apache/hadoop-yarn-applications-catalog-docker</repository>
+ <tag>${project.version}</tag>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/src/main/resources/jaas.config b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/src/main/resources/jaas.config
new file mode 100644
index 0000000..937df57
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/src/main/resources/jaas.config
@@ -0,0 +1,9 @@
+com.sun.security.jgss.krb5.initiate {
+ com.sun.security.auth.module.Krb5LoginModule required
+ doNotPrompt=true
+ principal="${PRINCIPAL}"
+ useKeyTab=true
+ keyTab="${KEYTAB}"
+ storeKey=true
+ useTicketCache=false;
+};
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/src/main/resources/samples.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/src/main/resources/samples.xml
new file mode 100644
index 0000000..5389cfb
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/src/main/resources/samples.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<add>
+ <doc>
+ <field name="id">appStore_1</field>
+ <field name="type_s">AppStoreEntry</field>
+ <field name="org_s">Apache</field>
+ <field name="name_s">Http Server Project</field>
+ <field name="desc_s">The Number One HTTP Server On The Internet</field>
+ <field name="like_i">50000</field>
+ <field name="download_i">100000</field>
+ <field name="yarnfile_s">{
+ "name": "httpd",
+ "version": "1.0",
+ "components" :
+ [
+ {
+ "name": "httpd",
+ "number_of_containers": 2,
+ "artifact": {
+ "id": "centos/httpd-24-centos7:latest",
+ "type": "DOCKER"
+ },
+ "resource": {
+ "cpus": 1,
+ "memory": "256"
+ },
+ "configuration": {
+ "env": {
+ "YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE":"true",
+ "YARN_CONTAINER_RUNTIME_DOCKER_PORTS_MAPPING":"8080:8080"
+ },
+ "properties": {
+ }
+ }
+ }
+ ],
+ "quicklinks": {
+ "Httpd UI": "http://httpd.${SERVICE_NAME}.${USER}.${DOMAIN}:8080/"
+ }
+}
+ </field>
+ </doc>
+ <doc>
+ <field name="id">appStore_2</field>
+ <field name="type_s">AppStoreEntry</field>
+ <field name="org_s">Jenkins-ci.org</field>
+ <field name="name_s">Jenkins</field>
+ <field name="desc_s">The leading open source automation server</field>
+ <field name="like_i">100000</field>
+ <field name="download_i">1000000</field>
+ <field name="yarnfile_s">{
+ "name": "httpd",
+ "version": "1.0",
+ "components" :
+ [
+ {
+ "name": "jenkins",
+ "number_of_containers": 1,
+ "artifact": {
+ "id": "jenkins/jenkins:latest",
+ "type": "DOCKER"
+ },
+ "resource": {
+ "cpus": 1,
+ "memory": "1024"
+ },
+ "run_privileged_container": true,
+ "configuration": {
+ "env": {
+ "YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE":"true",
+ "YARN_CONTAINER_RUNTIME_DOCKER_PORTS_MAPPING":"8080:8080"
+ },
+ "properties": {
+ }
+ }
+ }
+ ],
+ "quicklinks": {
+ "Jenkins UI": "http://jenkins.${SERVICE_NAME}.${USER}.${DOMAIN}:8080/"
+ }
+}
+ </field>
+ </doc>
+ <doc>
+ <field name="id">appStore_3</field>
+ <field name="type_s">AppStoreEntry</field>
+ <field name="org_s">Docker</field>
+ <field name="name_s">Registry</field>
+ <field name="desc_s">The Docker Registry 2.0 implementation for storing and distributing Docker images.</field>
+ <field name="like_i">360</field>
+ <field name="download_i">1</field>
+ <field name="yarnfile_s">{
+ "name": "docker-registry",
+ "version": "1.0",
+ "components" :
+ [
+ {
+ "name": "registry",
+ "number_of_containers": 1,
+ "artifact": {
+ "id": "registry:latest",
+ "type": "DOCKER"
+ },
+ "resource": {
+ "cpus": 1,
+ "memory": "1024"
+ },
+ "configuration": {
+ "env": {
+ "YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE":"true",
+ "YARN_CONTAINER_RUNTIME_DOCKER_PORTS_MAPPING":"5000:5000"
+ },
+ "properties": {
+ }
+ }
+ }
+ ]
+}
+ </field>
+ </doc>
+</add>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/src/main/scripts/entrypoint.sh b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/src/main/scripts/entrypoint.sh
new file mode 100755
index 0000000..1666063
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/src/main/scripts/entrypoint.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+template_generator() {
+ REGEX='(\$\{[a-zA-Z_][a-zA-Z_0-9]*\})'
+ if [ -e "$2" ]; then
+ mv -f "$2" "$2.bak"
+ fi
+ while IFS='' read -r line || [[ -n "$line" ]]; do
+ while [[ "$line" =~ $REGEX ]] ; do
+ LHS=${BASH_REMATCH[1]}
+ RHS="$(eval echo "\"$LHS\"")"
+ line=${line//$LHS/$RHS}
+ done
+ echo "$line" >> "$2"
+ done < "$1"
+}
+
+export JAVA_HOME=/usr/lib/jvm/jre
+export HADOOP_CONF_DIR=/etc/hadoop/conf
+/opt/apache/solr/bin/solr start -p 8983 -force
+/opt/apache/solr/bin/solr create_core -c appcatalog -force
+/opt/apache/solr/bin/post -c appcatalog /tmp/samples.xml
+if [ -d /etc/hadoop/conf ]; then
+ sed -i.bak 's/shared.loader=.*$/shared.loader=\/etc\/hadoop\/conf/g' /etc/tomcat/catalina.properties
+fi
+if [ -e "$KEYTAB" ]; then
+ export JAVA_OPTS="$JAVA_OPTS -Djava.security.auth.login.config=/etc/tomcat/jaas.config -Djava.security.krb5.conf=/etc/krb5.conf -Djavax.security.auth.useSubjectCredsOnly=false"
+ template_generator /etc/tomcat/jaas.config.template /etc/tomcat/jaas.config
+fi
+/usr/libexec/tomcat/server start
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/src/main/scripts/setup-image.sh b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/src/main/scripts/setup-image.sh
new file mode 100755
index 0000000..c9376f9
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-docker/src/main/scripts/setup-image.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+mkdir -p /etc/hadoop
+mkdir -p /opt/apache/solr/server/logs
+chmod -R 777 /opt/apache/solr/server/logs /var/log/tomcat /var/cache/tomcat /var/lib/tomcat/webapps /opt/apache/solr/server/solr
+chmod 777 /etc/tomcat
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/.gitignore b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/.gitignore
new file mode 100644
index 0000000..a82f945
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/.gitignore
@@ -0,0 +1,3 @@
+.classpath
+.project
+/target/
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/.yarnrc b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/.yarnrc
new file mode 100644
index 0000000..e0ca4e9
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/.yarnrc
@@ -0,0 +1 @@
+--modules-folder target/generated-sources/vendor
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/README.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/README.md
new file mode 100644
index 0000000..58fe29f
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/README.md
@@ -0,0 +1,51 @@
+<!---
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. See accompanying LICENSE file.
+-->
+# Apache Hadoop YARN Application Catalog
+
+## Introduction
+
+Hadoop YARN Application Catalog is application catalog for
+deploying docker enabled cloud application on Hadoop.
+
+check it out:
+
+```bash
+git clone https://github.com/apache/hadoop.git
+```
+
+## Prerequisites
+* Firefox or Chrome
+* [npm](https://www.npmjs.org)
+* [nodejs](http://nodejs.org)
+* [JDK](http://www.oracle.com/technetwork/java/javaee/downloads/index.html)
+* [IDE](http://www.jetbrains.com/)
+* [bower](http://bower.io)
+* [PhantomJs](http://phantomjs.org) or `brew install phantomjs`
+* [Application Server](https://glassfish.java.net/download.html) or `brew install glassfish`
+
+## Installation
+
+```bash
+mvn package
+```
+
+When running this command a couple of things happen:
+* Npm and yarnpkg install will be run
+* JSLint will be run in src/main/javascript sources
+* Javascript will be minified
+* All the other standard maven phases.
+
+## Status of the project
+
+See Apache [JIRA](http://issues.apache.org/jira/browse/YARN)
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/dev-support/findbugs-exclude.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/dev-support/findbugs-exclude.xml
new file mode 100644
index 0000000..b89146a
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/dev-support/findbugs-exclude.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FindBugsFilter>
+
+</FindBugsFilter>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/package.json b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/package.json
new file mode 100644
index 0000000..e8781e0
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/package.json
@@ -0,0 +1,40 @@
+{
+ "name": "AppCatalog",
+ "private": true,
+ "version": "0.0.0",
+ "description": "YARN App Catalog",
+ "repository": "git://git.apache.org/hadoop.git",
+ "license": "Apache 2.0",
+ "dependencies": {
+ "jquery": "3.3.1",
+ "angular-loader": "~1.6.4",
+ "angular-mocks": "~1.6.4",
+ "angular-route": "~1.6.4",
+ "angular": "~1.6.4",
+ "bootstrap": "~3.3.7",
+ "roboto-fontface": "0.10.0"
+ },
+ "devDependencies": {
+ "http-server": "^0.6.1",
+ "requirejs": "^2.1.0",
+ "karma": "4.0.0",
+ "karma-requirejs": "^0.2.2",
+ "karma-script-launcher": "^0.1.0",
+ "karma-chrome-launcher": "^0.1.4",
+ "karma-firefox-launcher": "^0.1.3",
+ "karma-jasmine": "^0.1.5",
+ "karma-junit-reporter": "^0.2.2",
+ "shelljs": "^0.2.6",
+ "apidoc": "0.17.7"
+ },
+ "scripts": {
+ "prestart": "npm install & mvn clean package",
+ "start": "http-server target/app -a localhost -p 8000",
+ "pretest": "npm install",
+ "test": "karma start src/test/javascript/karma.conf.js",
+ "test-single-run": "karma start src/test/javascript/karma.conf.js --single-run",
+ "preupdate-webdriver": "npm install",
+ "update-webdriver": "webdriver-manager update",
+ "update-index-async": "node -e \"require('shelljs/global'); sed('-i', /\\/\\/@@NG_LOADER_START@@[\\s\\S]*\\/\\/@@NG_LOADER_END@@/, '//@@NG_LOADER_START@@\\n' + cat('src/main/webapp/vendor/angular-loader/angular-loader.min.js') + '\\n//@@NG_LOADER_END@@', 'src/main/webapp/index.html');\""
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/pom.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/pom.xml
new file mode 100644
index 0000000..8e0cae7
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/pom.xml
@@ -0,0 +1,455 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-yarn-applications-catalog-webapp</artifactId>
+ <packaging>war</packaging>
+
+ <parent>
+ <artifactId>hadoop-yarn-applications-catalog</artifactId>
+ <groupId>org.apache.hadoop</groupId>
+ <version>3.3.0-SNAPSHOT</version>
+ </parent>
+
+ <name>YARN Application Catalog Webapp</name>
+
+ <url>http://hadoop.apache.org</url>
+
+ <properties>
+ <artifact.name>app</artifact.name>
+ <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
+ <vendor.loc>target/generated-sources/vendor</vendor.loc>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <dependency.locations.enabled>false</dependency.locations.enabled>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>io.swagger</groupId>
+ <artifactId>swagger-annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-module-junit4</artifactId>
+ <version>${powermock.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.powermock</groupId>
+ <artifactId>powermock-api-mockito</artifactId>
+ <version>${powermock.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ </exclusion>
+ </exclusions>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.glassfish.jersey.media</groupId>
+ <artifactId>jersey-media-json-jackson</artifactId>
+ <version>2.12</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>org.javassist</groupId>
+ <artifactId>javassist</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-base</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>com.sun.jersey</groupId>
+ <artifactId>jersey-json</artifactId>
+ <version>${jersey.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.solr</groupId>
+ <artifactId>solr-solrj</artifactId>
+ <version>${solr.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.solr</groupId>
+ <artifactId>solr-core</artifactId>
+ <version>${solr.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ </exclusion>
+ </exclusions>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.solr</groupId>
+ <artifactId>solr-test-framework</artifactId>
+ <version>${solr.version}</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-io</artifactId>
+ </exclusion>
+ </exclusions>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>jcl-over-slf4j</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>${slf4j.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-common</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-auth</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-yarn-common</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-yarn-services-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-yarn-services-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ <version>${jackson2.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ <version>${jackson2.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>${jackson2.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-json-provider</artifactId>
+ <version>${jackson2.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.jaxrs</groupId>
+ <artifactId>jackson-jaxrs-base</artifactId>
+ <version>${jackson2.version}</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <finalName>${artifact.name}</finalName>
+ <resources>
+ <resource>
+ <directory>src/main/javascript</directory>
+ <filtering>true</filtering>
+ </resource>
+ <resource>
+ <directory>src/main/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>com.github.eirslett</groupId>
+ <artifactId>frontend-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>install node and yarn</id>
+ <goals>
+ <goal>install-node-and-yarn</goal>
+ </goals>
+ <phase>generate-resources</phase>
+ <configuration>
+ <nodeVersion>v8.11.3</nodeVersion>
+ <yarnVersion>v1.7.0</yarnVersion>
+ </configuration>
+ </execution>
+ <execution>
+ <id>yarn install</id>
+ <goals>
+ <goal>yarn</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.8</source>
+ <target>1.8</target>
+ <compilerArguments>
+ <endorseddirs>${endorsed.dir}</endorseddirs>
+ </compilerArguments>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <log4j.configuration>file:${project.build.testOutputDirectory}/log4j.properties</log4j.configuration>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-war-plugin</artifactId>
+ <configuration>
+ <failOnMissingWebXml>false</failOnMissingWebXml>
+ <webResources>
+ <resource>
+ <directory>target/generated-sources</directory>
+ <includes>
+ <include>vendor/jquery/**</include>
+ <include>vendor/angular/**</include>
+ <include>vendor/angular-route/**</include>
+ <include>vendor/bootstrap/**</include>
+ <include>vendor/roboto-fontface/fonts/roboto/Roboto-Regular.woff</include>
+ </includes>
+ </resource>
+ </webResources>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>validate</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${endorsed.dir}</outputDirectory>
+ <silent>true</silent>
+ <artifactItems>
+ <artifactItem>
+ <groupId>javax</groupId>
+ <artifactId>javaee-endorsed-api</artifactId>
+ <version>7.0</version>
+ <type>jar</type>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>net.alchim31.maven</groupId>
+ <artifactId>yuicompressor-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>compress-js</id>
+ <phase>process-resources</phase>
+ <goals>
+ <!--<goal>jslint</goal>-->
+ <goal>compress</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <failOnWarning>true</failOnWarning>
+ <!--<outputDirectory>src/main/webapp/js</outputDirectory>-->
+ <outputDirectory>target/${artifact.name}/js</outputDirectory>
+ <nosuffix>true</nosuffix>
+ <excludes>
+ <exclude>**/js/**/*js</exclude>
+ <exclude>vendor/**</exclude>
+ <exclude>**/*min.css</exclude>
+ <exclude>**/*min.js</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>com.github.klieber</groupId>
+ <artifactId>phantomjs-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>install</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <version>2.1.1</version>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>com.github.searls</groupId>
+ <artifactId>jasmine-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test</goal>
+ <goal>bdd</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <webDriverClassName>org.openqa.selenium.phantomjs.PhantomJSDriver</webDriverClassName>
+ <webDriverCapabilities>
+ <capability>
+ <name>phantomjs.binary.path</name>
+ <value>${phantomjs.binary}</value>
+ </capability>
+ </webDriverCapabilities>
+ <preloadSources>
+ <source>${vendor.loc}/jquery/dist/jquery.js</source>
+ <source>${vendor.loc}/angular/angular.min.js</source>
+ <source>${vendor.loc}/angular-route/angular-route.min.js</source>
+ <source>${vendor.loc}/angular-mocks/angular-mocks.js</source>
+ </preloadSources>
+ <jsSrcDir>src/main/javascript</jsSrcDir>
+ <jsTestSrcDir>src/test/javascript</jsTestSrcDir>
+ <specIncludes>
+ <include>*Spec.js</include>
+ </specIncludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>src/main/webapp/vendor/**</exclude>
+ <exclude>src/main/resources/appcatalog.properties</exclude>
+ <exclude>src/main/webapp//**/*.svg</exclude>
+ <exclude>src/test/javascript/karma.conf.js</exclude>
+ <exclude>src/test/resources/configsets/exampleCollection/conf/params.json</exclude>
+ <exclude>src/test/resources/log4j.properties</exclude>
+ <exclude>src/test/resources/jaas.config</exclude>
+ <exclude>.bowerrc</exclude>
+ <exclude>.yarnrc</exclude>
+ <exclude>bower.json</exclude>
+ <exclude>package.json</exclude>
+ <exclude>yarn.lock</exclude>
+ <exclude>node/**</exclude>
+ <exclude>node_modules/**</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <excludePackageNames>org.apache.hadoop.yarn.appcatalog</excludePackageNames>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+<!--
+ <profiles>
+ <profile>
+ <id>rest-docs</id>
+ <activation>
+ <file>
+ <exists>${basedir}/node_modules/.bin/apidoc</exists>
+ </file>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>apidocs</id>
+ <phase>package</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <outputFile>${project.build.directory}/apidocs.log</outputFile>
+ <executable>${basedir}/node/node</executable>
+ <commandlineArgs>${basedir}/node_modules/.bin/apidoc -i src/main/java -o ${project.build.directory}/site/apidocs</commandlineArgs>
+ <silent>true</silent>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+-->
+
+</project>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/AppCatalog.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/AppCatalog.java
new file mode 100644
index 0000000..07e3a6f
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/AppCatalog.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.yarn.appcatalog.application;
+
+import javax.ws.rs.ApplicationPath;
+import javax.ws.rs.core.Application;
+
+import org.apache.hadoop.yarn.appcatalog.controller.AppDetailsController;
+
+import java.util.Set;
+
+/**
+ * Jackson resource configuration class for Application Catalog.
+ */
+@ApplicationPath("service")
+public class AppCatalog extends Application {
+
+ @Override
+ public Set<Class<?>> getClasses() {
+ final Set<Class<?>> resources = new java.util.HashSet<>();
+ // following code can be used to customize Jersey 2.0 JSON provider:
+ try {
+ final Class<?> jsonProvider =
+ Class.forName("org.glassfish.jersey.jackson.JacksonFeature");
+ // Class jsonProvider =
+ // Class.forName("org.glassfish.jersey.moxy.json.MoxyJsonFeature");
+ // Class jsonProvider =
+ // Class.forName("org.glassfish.jersey.jettison.JettisonFeature");
+ resources.add(jsonProvider);
+ } catch (final ClassNotFoundException ex) {
+ ex.printStackTrace();
+ }
+ addRestResourceClasses(resources);
+ return resources;
+ }
+
+ /**
+ * Add your own resources here.
+ */
+ private void addRestResourceClasses(final Set<Class<?>> resources) {
+ resources.add(AppDetailsController.class);
+ }
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/AppCatalogSolrClient.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/AppCatalogSolrClient.java
new file mode 100644
index 0000000..1397722
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/AppCatalogSolrClient.java
@@ -0,0 +1,359 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.hadoop.yarn.appcatalog.application;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.Random;
+
+import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
+import org.apache.hadoop.yarn.appcatalog.model.AppStoreEntry;
+import org.apache.hadoop.yarn.appcatalog.model.Application;
+import org.apache.hadoop.yarn.appcatalog.utils.RandomWord;
+import org.apache.hadoop.yarn.appcatalog.utils.WordLengthException;
+import org.apache.hadoop.yarn.service.api.records.Service;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrQuery;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.impl.HttpSolrClient;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.client.solrj.response.UpdateResponse;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrInputDocument;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Driver class for accessing Solr.
+ */
+public class AppCatalogSolrClient {
+
+ private static final Log LOG = LogFactory.getLog(AppCatalogSolrClient.class);
+ private static String urlString;
+
+ public AppCatalogSolrClient() {
+ // Locate Solr URL
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+ InputStream input =
+ classLoader.getResourceAsStream("appcatalog.properties");
+ Properties properties = new Properties();
+ try {
+ properties.load(input);
+ urlString = properties.getProperty("solr_url");
+ } catch (IOException e) {
+ LOG.error("Error reading appcatalog configuration: ", e);
+ }
+ }
+
+ public SolrClient getSolrClient() {
+ return new HttpSolrClient.Builder(urlString).build();
+ }
+
+ public List<AppStoreEntry> getRecommendedApps() {
+ List<AppStoreEntry> apps = new ArrayList<AppStoreEntry>();
+ SolrClient solr = getSolrClient();
+ SolrQuery query = new SolrQuery();
+ query.setQuery("*:*");
+ query.setFilterQueries("type_s:AppStoreEntry");
+ query.setRows(40);
+ QueryResponse response;
+ try {
+ response = solr.query(query);
+ Iterator<SolrDocument> list = response.getResults().listIterator();
+ while (list.hasNext()) {
+ SolrDocument d = list.next();
+ AppStoreEntry entry = new AppStoreEntry();
+ entry.setId(d.get("id").toString());
+ entry.setOrg(d.get("org_s").toString());
+ entry.setName(d.get("name_s").toString());
+ entry.setDesc(d.get("desc_s").toString());
+ if (d.get("icon_s")!=null) {
+ entry.setIcon(d.get("icon_s").toString());
+ }
+ entry.setLike(Integer.valueOf(d.get("like_i").toString()));
+ entry.setDownload(Integer.valueOf(d.get("download_i").toString()));
+ apps.add(entry);
+ }
+ } catch (SolrServerException | IOException e) {
+ LOG.error("Error getting a list of recommended applications: ", e);
+ }
+ return apps;
+ }
+
+ public List<AppStoreEntry> search(String keyword) {
+ List<AppStoreEntry> apps = new ArrayList<AppStoreEntry>();
+ SolrClient solr = getSolrClient();
+ SolrQuery query = new SolrQuery();
+ if (keyword.length()==0) {
+ query.setQuery("*:*");
+ query.setFilterQueries("type_s:AppStoreEntry");
+ } else {
+ query.setQuery(keyword);
+ query.setFilterQueries("type_s:AppStoreEntry");
+ }
+ query.setRows(40);
+ QueryResponse response;
+ try {
+ response = solr.query(query);
+ Iterator<SolrDocument> list = response.getResults().listIterator();
+ while (list.hasNext()) {
+ SolrDocument d = list.next();
+ AppStoreEntry entry = new AppStoreEntry();
+ entry.setId(d.get("id").toString());
+ entry.setOrg(d.get("org_s").toString());
+ entry.setName(d.get("name_s").toString());
+ entry.setDesc(d.get("desc_s").toString());
+ entry.setLike(Integer.valueOf(d.get("like_i").toString()));
+ entry.setDownload(Integer.valueOf(d.get("download_i").toString()));
+ apps.add(entry);
+ }
+ } catch (SolrServerException | IOException e) {
+ LOG.error("Error in searching for applications: ", e);
+ }
+ return apps;
+ }
+
+ public List<AppEntry> listAppEntries() {
+ List<AppEntry> list = new ArrayList<AppEntry>();
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+ SolrClient solr = getSolrClient();
+ SolrQuery query = new SolrQuery();
+ query.setQuery("*:*");
+ query.setFilterQueries("type_s:AppEntry");
+ query.setRows(40);
+ QueryResponse response;
+ try {
+ response = solr.query(query);
+ Iterator<SolrDocument> appList = response.getResults().listIterator();
+ while (appList.hasNext()) {
+ SolrDocument d = appList.next();
+ AppEntry entry = new AppEntry();
+ entry.setId(d.get("id").toString());
+ entry.setName(d.get("name_s").toString());
+ entry.setApp(d.get("app_s").toString());
+ entry.setYarnfile(mapper.readValue(d.get("yarnfile_s").toString(),
+ Service.class));
+ list.add(entry);
+ }
+ } catch (SolrServerException | IOException e) {
+ LOG.error("Error in listing deployed applications: ", e);
+ }
+ return list;
+ }
+
+ public AppStoreEntry findAppStoreEntry(String id) {
+ AppStoreEntry entry = new AppStoreEntry();
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+ SolrClient solr = getSolrClient();
+ SolrQuery query = new SolrQuery();
+ query.setQuery("id:" + id);
+ query.setFilterQueries("type_s:AppStoreEntry");
+ query.setRows(1);
+
+ QueryResponse response;
+ try {
+ response = solr.query(query);
+ Iterator<SolrDocument> appList = response.getResults().listIterator();
+ while (appList.hasNext()) {
+ SolrDocument d = appList.next();
+ entry.setId(d.get("id").toString());
+ entry.setOrg(d.get("org_s").toString());
+ entry.setName(d.get("name_s").toString());
+ entry.setDesc(d.get("desc_s").toString());
+ entry.setLike(Integer.valueOf(d.get("like_i").toString()));
+ entry.setDownload(Integer.valueOf(d.get("download_i").toString()));
+ Service yarnApp = mapper.readValue(d.get("yarnfile_s").toString(),
+ Service.class);
+ String name;
+ try {
+ Random r = new Random();
+ int low = 3;
+ int high = 10;
+ int seed = r.nextInt(high-low) + low;
+ int seed2 = r.nextInt(high-low) + low;
+ name = RandomWord.getNewWord(seed).toLowerCase() + "-" + RandomWord
+ .getNewWord(seed2).toLowerCase();
+ } catch (WordLengthException e) {
+ name = "c" + java.util.UUID.randomUUID().toString().substring(0, 11);
+ }
+ yarnApp.setName(name);
+ entry.setApp(yarnApp);
+ }
+ } catch (SolrServerException | IOException e) {
+ LOG.error("Error in finding deployed application: " + id, e);
+ }
+ return entry;
+ }
+
+ public AppEntry findAppEntry(String id) {
+ AppEntry entry = new AppEntry();
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+ SolrClient solr = getSolrClient();
+ SolrQuery query = new SolrQuery();
+ query.setQuery("id:" + id);
+ query.setFilterQueries("type_s:AppEntry");
+ query.setRows(1);
+
+ QueryResponse response;
+ try {
+ response = solr.query(query);
+ Iterator<SolrDocument> appList = response.getResults().listIterator();
+ while (appList.hasNext()) {
+ SolrDocument d = appList.next();
+ entry.setId(d.get("id").toString());
+ entry.setApp(d.get("app_s").toString());
+ entry.setName(d.get("name_s").toString());
+ entry.setYarnfile(mapper.readValue(d.get("yarnfile_s").toString(),
+ Service.class));
+ }
+ } catch (SolrServerException | IOException e) {
+ LOG.error("Error in finding deployed application: " + id, e);
+ }
+ return entry;
+ }
+
+ public void deployApp(String id, Service service) throws SolrServerException,
+ IOException {
+ long download = 0;
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ Collection<SolrInputDocument> docs = new HashSet<SolrInputDocument>();
+ SolrClient solr = getSolrClient();
+ // Find application information from AppStore
+ SolrQuery query = new SolrQuery();
+ query.setQuery("id:" + id);
+ query.setFilterQueries("type_s:AppStoreEntry");
+ query.setRows(1);
+ QueryResponse response = solr.query(query);
+ Iterator<SolrDocument> appList = response.getResults().listIterator();
+ AppStoreEntry entry = new AppStoreEntry();
+ while (appList.hasNext()) {
+ SolrDocument d = appList.next();
+ entry.setOrg(d.get("org_s").toString());
+ entry.setName(d.get("name_s").toString());
+ entry.setDesc(d.get("desc_s").toString());
+ entry.setLike(Integer.valueOf(d.get("like_i").toString()));
+ entry.setDownload(Integer.valueOf(d.get("download_i").toString()));
+ download = entry.getDownload() + 1;
+
+ // Update download count
+ docs.add(incrementDownload(d, download));
+ }
+
+ // increment download count for application
+
+ if (service!=null) {
+ // Register deployed application instance with AppList
+ SolrInputDocument request = new SolrInputDocument();
+ String name = service.getName();
+ request.addField("type_s", "AppEntry");
+ request.addField("id", name);
+ request.addField("name_s", name);
+ request.addField("app_s", entry.getOrg()+"/"+entry.getName());
+ request.addField("yarnfile_s", mapper.writeValueAsString(service));
+ docs.add(request);
+ }
+
+ // Commit Solr changes.
+ UpdateResponse detailsResponse = solr.add(docs);
+ if (detailsResponse.getStatus() != 0) {
+ throw new IOException("Unable to register docker instance "
+ + "with application entry.");
+ }
+ solr.commit();
+ }
+
+ private SolrInputDocument incrementDownload(SolrDocument doc,
+ long download) {
+ Collection<String> names = doc.getFieldNames();
+ SolrInputDocument s = new SolrInputDocument();
+ for (String name : names) {
+ if(!name.equals("_version_")) {
+ s.addField(name, doc.getFieldValues(name));
+ }
+ }
+ s.setField("download_i", download++);
+ return s;
+ }
+
+ public void deleteApp(String id) {
+ SolrClient solr = getSolrClient();
+ try {
+ solr.deleteById(id);
+ solr.commit();
+ } catch (SolrServerException | IOException e) {
+ LOG.error("Error in removing deployed application: "+id, e);
+ }
+ }
+
+ public void register(Application app) throws IOException {
+ Collection<SolrInputDocument> docs = new HashSet<SolrInputDocument>();
+ SolrClient solr = getSolrClient();
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ try {
+ SolrInputDocument buffer = new SolrInputDocument();
+ buffer.setField("id", java.util.UUID.randomUUID().toString()
+ .substring(0, 11));
+ buffer.setField("org_s", app.getOrganization());
+ buffer.setField("name_s", app.getName());
+ buffer.setField("desc_s", app.getDescription());
+ if (app.getIcon() != null) {
+ buffer.setField("icon_s", app.getIcon());
+ }
+ buffer.setField("type_s", "AppStoreEntry");
+ buffer.setField("like_i", 0);
+ buffer.setField("download_i", 0);
+
+ // Keep only YARN data model for yarnfile field
+ String yarnFile = mapper.writeValueAsString(app);
+ LOG.info("app:"+yarnFile);
+ Service yarnApp = mapper.readValue(yarnFile, Service.class);
+ buffer.setField("yarnfile_s", mapper.writeValueAsString(yarnApp));
+
+ docs.add(buffer);
+ // Commit Solr changes.
+ UpdateResponse detailsResponse = solr.add(docs);
+ if (detailsResponse.getStatus() != 0) {
+ throw new IOException("Unable to register application " +
+ "in Application Store.");
+ }
+ solr.commit();
+ } catch (SolrServerException | IOException e) {
+ throw new IOException("Unable to register application " +
+ "in Application Store. "+ e.getMessage());
+ }
+ }
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/YarnServiceClient.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/YarnServiceClient.java
new file mode 100644
index 0000000..667d218
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/YarnServiceClient.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.hadoop.yarn.appcatalog.application;
+
+import java.io.IOException;
+
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
+import org.apache.hadoop.yarn.service.api.records.Service;
+import org.apache.hadoop.yarn.service.api.records.KerberosPrincipal;
+import org.apache.hadoop.yarn.service.client.ApiServiceClient;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.sun.jersey.api.client.ClientHandlerException;
+import com.sun.jersey.api.client.ClientResponse;
+import com.sun.jersey.api.client.UniformInterfaceException;
+import com.sun.jersey.api.client.config.ClientConfig;
+import com.sun.jersey.api.client.config.DefaultClientConfig;
+
+/**
+ * Driver class for calling YARN Resource Manager REST API.
+ */
+public class YarnServiceClient {
+
+ private static final Log LOG = LogFactory.getLog(YarnServiceClient.class);
+ private static Configuration conf = new Configuration();
+ private static ClientConfig getClientConfig() {
+ ClientConfig config = new DefaultClientConfig();
+ config.getProperties().put(ClientConfig.PROPERTY_CHUNKED_ENCODING_SIZE, 0);
+ config.getProperties()
+ .put(ClientConfig.PROPERTY_BUFFER_RESPONSE_ENTITY_ON_EXCEPTION, true);
+ return config;
+ }
+
+ private ApiServiceClient asc;
+
+ public YarnServiceClient() {
+ try {
+ asc = new ApiServiceClient(conf);
+ } catch (Exception e) {
+ LOG.error("Error initialize YARN Service Client: {}", e);
+ }
+ }
+
+ public void createApp(Service app) {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ ClientResponse response;
+ try {
+ boolean useKerberos = UserGroupInformation.isSecurityEnabled();
+ if (useKerberos) {
+ KerberosPrincipal kerberos = new KerberosPrincipal();
+ String[] temp = System.getenv("PRINCIPAL").split("@");
+ String[] temp2 = temp[0].split("/");
+ StringBuilder sb = new StringBuilder();
+ sb.append(temp2[0]);
+ sb.append("/");
+ sb.append("_HOST");
+ sb.append("@");
+ sb.append(temp[1]);
+ String keytab = System.getenv("KEYTAB");
+ if (!keytab.startsWith("file://")) {
+ keytab = "file://" + keytab;
+ }
+ kerberos.setPrincipalName(sb.toString());
+ kerberos.setKeytab(keytab);
+ app.setKerberosPrincipal(kerberos);
+ }
+ response = asc.getApiClient().post(ClientResponse.class,
+ mapper.writeValueAsString(app));
+ if (response.getStatus() >= 299) {
+ String message = response.getEntity(String.class);
+ throw new RuntimeException("Failed : HTTP error code : "
+ + response.getStatus() + " error: " + message);
+ }
+ } catch (UniformInterfaceException | ClientHandlerException
+ | IOException e) {
+ LOG.error("Error in deploying application: ", e);
+ }
+ }
+
+ public void deleteApp(String appInstanceId) {
+ ClientResponse response;
+ try {
+ response = asc.getApiClient(asc.getServicePath(appInstanceId))
+ .delete(ClientResponse.class);
+ if (response.getStatus() >= 299) {
+ String message = response.getEntity(String.class);
+ throw new RuntimeException("Failed : HTTP error code : "
+ + response.getStatus() + " error: " + message);
+ }
+ } catch (UniformInterfaceException | ClientHandlerException
+ | IOException e) {
+ LOG.error("Error in deleting application: ", e);
+ }
+ }
+
+ public void restartApp(Service app) throws JsonProcessingException {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ String appInstanceId = app.getName();
+ String yarnFile = mapper.writeValueAsString(app);
+ ClientResponse response;
+ try {
+ response = asc.getApiClient(asc.getServicePath(appInstanceId))
+ .put(ClientResponse.class, yarnFile);
+ if (response.getStatus() >= 299) {
+ String message = response.getEntity(String.class);
+ throw new RuntimeException("Failed : HTTP error code : "
+ + response.getStatus() + " error: " + message);
+ }
+ } catch (UniformInterfaceException | ClientHandlerException
+ | IOException e) {
+ LOG.error("Error in restarting application: ", e);
+ }
+ }
+
+ public void stopApp(Service app) throws JsonProcessingException {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ String appInstanceId = app.getName();
+ String yarnFile = mapper.writeValueAsString(app);
+ ClientResponse response;
+ try {
+ response = asc.getApiClient(asc.getServicePath(appInstanceId))
+ .put(ClientResponse.class, yarnFile);
+ if (response.getStatus() >= 299) {
+ String message = response.getEntity(String.class);
+ throw new RuntimeException("Failed : HTTP error code : "
+ + response.getStatus() + " error: " + message);
+ }
+ } catch (UniformInterfaceException | ClientHandlerException
+ | IOException e) {
+ LOG.error("Error in stopping application: ", e);
+ }
+ }
+
+ public void getStatus(AppEntry entry) {
+ ObjectMapper mapper = new ObjectMapper();
+ mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+ String appInstanceId = entry.getName();
+ Service app = null;
+ try {
+ String yarnFile = asc.getApiClient(asc.getServicePath(appInstanceId))
+ .get(String.class);
+ app = mapper.readValue(yarnFile, Service.class);
+ entry.setYarnfile(app);
+ } catch (UniformInterfaceException | IOException e) {
+ LOG.error("Error in fetching application status: ", e);
+ }
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/package-info.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/package-info.java
new file mode 100644
index 0000000..e8077ca
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/application/package-info.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+/**
+ * Application Catalog.
+ */
+package org.apache.hadoop.yarn.appcatalog.application;
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/AppDetailsController.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/AppDetailsController.java
new file mode 100644
index 0000000..db6973d
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/AppDetailsController.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.hadoop.yarn.appcatalog.controller;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.hadoop.yarn.appcatalog.application.AppCatalogSolrClient;
+import org.apache.hadoop.yarn.appcatalog.application.YarnServiceClient;
+import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
+import org.apache.hadoop.yarn.service.api.records.Service;
+import org.apache.hadoop.yarn.service.api.records.ServiceState;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+
+/**
+ * Application catalog REST API for displaying application details.
+ */
+@Path("/app_details")
+public class AppDetailsController {
+
+ public AppDetailsController() {
+ }
+
+ /**
+ * List detail information about the deployed application.
+ *
+ * @apiGroup AppDetailController
+ * @apiName getDetails
+ * @api {get} /app_details/config/{id} Check config of application instance.
+ * @apiParam {String} id Application ID to fetch configuration.
+ * @apiSuccess {Object} AppEntry Application configuration.
+ * @apiSuccessExample {json} Success-Response:
+ * HTTP/1.1 200 OK
+ * {
+ * "id":"howita_man",
+ * "name":"howita_man",
+ * "app":"Jenkins-ci.org/Jenkins",
+ * "yarnfile":{
+ * "name":"howita_man",
+ * "lifetime":3600,
+ * "containers":[
+ * ],
+ * "components":[
+ * {
+ * "name":"jenkins",
+ * "dependencies":[
+ * ],
+ * "artifact":{
+ * "id":"eyang-1.openstacklocal:5000/jenkins:latest",
+ * "type":"DOCKER"
+ * },
+ * "launch_command":"",
+ * "resource":{
+ * "uri":null,
+ * "profile":null,
+ * "cpus":1,
+ * "memory":"2048"
+ * },
+ * "number_of_containers":1,
+ * "run_privileged_container":false,
+ * "configuration":{
+ * "properties":{
+ * },
+ * "env":{
+ * },
+ * "files":[
+ * ]
+ * },
+ * "quicklinks":[
+ * ],
+ * "containers":[
+ * ]
+ * }
+ * ],
+ * "configuration":{
+ * "properties":{
+ * },
+ * "env":{
+ * },
+ * "files":[
+ * ]
+ * },
+ * "quicklinks":{
+ * "Jenkins UI":"http://jenkins.howita_man.yarn.${DOMAIN}:8080/"
+ * }
+ * }
+ * }
+ * @param id - Application ID
+ * @return application entry-
+ */
+ @Path("config/{id}")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public AppEntry getDetails(@PathParam("id") String id) {
+ AppCatalogSolrClient sc = new AppCatalogSolrClient();
+ return sc.findAppEntry(id);
+ }
+
+ /**
+ * Check application status.
+ *
+ * @apiGroup AppDetailController
+ * @apiName getStatus
+ * @api {get} /app_details/status/{id} Check status of application instance.
+ * @apiParam {String} id Application ID to check.
+ * @apiSuccessExample {json} Success-Response:
+ * HTTP/1.1 200 OK
+ * {
+ * "id":"howita_man",
+ * "name":"howita_man",
+ * "app":"Jenkins-ci.org/Jenkins",
+ * "yarnfile":{
+ * "name":"howita_man",
+ * "lifetime":3099,
+ * "containers":[
+ * ],
+ * "id":"application_1503694240849_0043",
+ * "components":[
+ * {
+ * "name":"jenkins",
+ * "dependencies":[
+ * ],
+ * "artifact":{
+ * "id":"eyang-1.openstacklocal:5000/jenkins:latest",
+ * "type":"DOCKER"
+ * },
+ * "launch_command":"",
+ * "resource":{
+ * "uri":null,
+ * "profile":null,
+ * "cpus":1,
+ * "memory":"2048"
+ * },
+ * "number_of_containers":1,
+ * "run_privileged_container":false,
+ * "configuration":{
+ * "properties":{
+ * },
+ * "env":{
+ * },
+ * "files":[
+ * ]
+ * },
+ * "quicklinks":[
+ * ],
+ * "containers":[
+ * {
+ * "id":"container_1503694240849_0043_01_000002",
+ * "launch_time":1504630535403,
+ * "bare_host":"eyang-4.openstacklocal",
+ * "state":"READY",
+ * "component_name":"jenkins-0"
+ * }
+ * ]
+ * }
+ * ],
+ * "configuration":{
+ * "properties":{
+ * },
+ * "env":{
+ * },
+ * "files":[
+ * ]
+ * },
+ * "quicklinks":{
+ * "Jenkins UI":"http://jenkins.howita_man.yarn.${DOMAIN}:8080/"
+ * }
+ * }
+ * }
+ * @apiSuccess {Object} text Give status
+ * @param id - Application ID
+ * @return application entry
+ */
+ @Path("status/{id}")
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public AppEntry getStatus(@PathParam("id") String id) {
+ AppCatalogSolrClient sc = new AppCatalogSolrClient();
+ AppEntry appEntry = sc.findAppEntry(id);
+ YarnServiceClient yc = new YarnServiceClient();
+ yc.getStatus(appEntry);
+ return appEntry;
+ }
+
+ /**
+ * Stop an application.
+ *
+ * @apiGroup AppDetailController
+ * @apiName stopApp
+ * @api {post} /app_details/stop/{id} Stop one instance of application.
+ * @apiParam {String} id Application ID to stop.
+ * @apiSuccess {String} text Give deployment status
+ * @apiError BadRequest Requested application does not stop.
+ * @param id - Application ID
+ * @return Web response code
+ */
+ @Path("stop/{id}")
+ @POST
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response stopApp(@PathParam("id") String id) {
+ AppCatalogSolrClient sc = new AppCatalogSolrClient();
+ AppEntry app = sc.findAppEntry(id);
+ Service yarnApp = app.getYarnfile();
+ yarnApp.setState(ServiceState.STOPPED);
+ try {
+ YarnServiceClient yc = new YarnServiceClient();
+ yc.stopApp(yarnApp);
+ } catch (JsonProcessingException e) {
+ return Response.status(Status.BAD_REQUEST).build();
+ }
+ return Response.ok().build();
+ }
+
+ /**
+ * Restart an application.
+ *
+ * @apiGroup AppDetailController
+ * @apiName restartApp
+ * @api {post} /app_details/restart/{id} Restart one instance of application.
+ * @apiParam {String} id Application ID to restart.
+ * @apiSuccess {String} text Give deployment status
+ * @apiError BadRequest Requested application does not restart.
+ * @param id - Application ID
+ * @return Web response code
+ */
+ @Path("restart/{id}")
+ @POST
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response restartApp(@PathParam("id") String id) {
+ AppCatalogSolrClient sc = new AppCatalogSolrClient();
+ AppEntry app = sc.findAppEntry(id);
+ Service yarnApp = app.getYarnfile();
+ yarnApp.setState(ServiceState.STARTED);
+ try {
+ YarnServiceClient yc = new YarnServiceClient();
+ yc.restartApp(yarnApp);
+ } catch (JsonProcessingException e) {
+ return Response.status(Status.BAD_REQUEST).build();
+ }
+ return Response.ok().build();
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/AppListController.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/AppListController.java
new file mode 100644
index 0000000..d818f23
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/AppListController.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.hadoop.yarn.appcatalog.controller;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.hadoop.yarn.appcatalog.application.AppCatalogSolrClient;
+import org.apache.hadoop.yarn.appcatalog.application.YarnServiceClient;
+import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
+import org.apache.hadoop.yarn.service.api.records.Service;
+import org.apache.solr.client.solrj.SolrServerException;
+
+/**
+ * Application deployment module.
+ */
+@Path("/app_list")
+@Produces({ MediaType.APPLICATION_JSON })
+public class AppListController {
+
+ public AppListController() {
+ }
+
+ /**
+ * Get Application List.
+ *
+ * @apiGroup AppListController
+ * @apiName get
+ * @api {get} /app_list Get list of deployed applications.
+ * @apiSuccess {Object[]} List<AppEntry> List of deployed Applications.
+ * @apiSuccessExample {json} Success-Response:
+ * HTTP/1.1 200 OK
+ * [
+ * {
+ * "id":"howita-man",
+ * "name":"howita-man",
+ * "app":"Jenkins-ci.org/Jenkins",
+ * "yarnfile":{
+ * "name":"howita_man",
+ * "lifetime":3600,
+ * "containers":[
+ * ],
+ * "components":[
+ * {
+ * "name":"jenkins",
+ * "dependencies":[
+ * ],
+ * "artifact":{
+ * "id":"eyang-1.openstacklocal:5000/jenkins:latest",
+ * "type":"DOCKER"
+ * },
+ * "launch_command":"",
+ * "resource":{
+ * "uri":null,
+ * "profile":null,
+ * "cpus":1,
+ * "memory":"2048"
+ * },
+ * "number_of_containers":1,
+ * "run_privileged_container":false,
+ * "configuration":{
+ * "properties":{
+ * },
+ * "env":{
+ * },
+ * "files":[
+ * ]
+ * },
+ * "quicklinks":[
+ * ],
+ * "containers":[
+ * ]
+ * }
+ * ],
+ * "configuration":{
+ * "properties":{
+ * },
+ * "env":{
+ * },
+ * "files":[
+ * ]
+ * },
+ * "quicklinks":{
+ * "Jenkins UI":"http://jenkins.${SERVICE_NAME}.${USER}.${DOMAIN}:8080/"
+ * }
+ * }
+ * },
+ * {
+ * ...
+ * }
+ * ]
+ * @return - Active application deployed by current user.
+ */
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<AppEntry> getList() {
+ AppCatalogSolrClient sc = new AppCatalogSolrClient();
+ return sc.listAppEntries();
+ }
+
+ /**
+ * Delete an application.
+ *
+ * @apiGroup AppListController
+ * @apiName delete
+ * @api {delete} /app_list Delete one instance of application.
+ * @apiParam {String} id Application name to delete.
+ * @apiSuccess {String} text Delete request accepted
+ * @param id - application ID
+ * @param name - application name
+ * @return Web response
+ */
+ @DELETE
+ @Path("{id}/{name}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response delete(@PathParam("id") String id,
+ @PathParam("name") String name) {
+ AppCatalogSolrClient sc = new AppCatalogSolrClient();
+ sc.deleteApp(id);
+ YarnServiceClient yc = new YarnServiceClient();
+ yc.deleteApp(name);
+ return Response.status(Status.ACCEPTED).build();
+ }
+
+ /**
+ * Deploy an application.
+ *
+ * @apiGroup AppListController
+ * @apiName deploy
+ * @api {post} /app_list/{id} Deploy one instance of application.
+ * @apiParam {String} id Application ID to deploy.
+ * @apiSuccess {String} text Give deployment status
+ * @apiError BadRequest Unable to deploy requested application.
+ * @param id - application ID
+ * @return Web response
+ */
+ @POST
+ @Path("{id}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response deploy(@PathParam("id") String id, Service service) {
+ AppCatalogSolrClient sc = new AppCatalogSolrClient();
+ try {
+ sc.deployApp(id, service);
+ } catch (SolrServerException | IOException e) {
+ return Response.status(Status.BAD_REQUEST).entity(e.toString()).build();
+ }
+ YarnServiceClient yc = new YarnServiceClient();
+ yc.createApp(service);
+ String output = "{\"status\":\"Application deployed.\",\"id\":\"" +
+ service.getName() + "\"}";
+ return Response.status(Status.ACCEPTED).entity(output).build();
+ }
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/AppStoreController.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/AppStoreController.java
new file mode 100644
index 0000000..5abb548
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/AppStoreController.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.hadoop.yarn.appcatalog.controller;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.hadoop.yarn.appcatalog.application.AppCatalogSolrClient;
+import org.apache.hadoop.yarn.appcatalog.model.AppStoreEntry;
+import org.apache.hadoop.yarn.appcatalog.model.Application;
+
+/**
+ * Application catalog REST API for searching and recommending
+ * applications.
+ *
+ */
+@Path("/app_store")
+public class AppStoreController {
+
+ public AppStoreController() {
+ }
+
+ /**
+ * Display the most frequently used applications on YARN AppCatalog home page.
+ *
+ * @apiGroup AppStoreController
+ * @apiName get
+ * @api {get} /app_store/recommended Display recommended applications.
+ * @apiSuccess {Object} AppEntry Application configuration.
+ * @apiSuccessExample {json} Success-Response:
+ * HTTP/1.1 200 OK
+ * [
+ * {
+ * "id":"96b7833a-e3",
+ * "org":"Hortonworks",
+ * "name":"LAMP",
+ * "desc":"Linux Apache MySQL PHP web application",
+ * "icon":"/css/img/feather.png",
+ * "like":0,
+ * "download":0,
+ * "app":null
+ * },
+ * {
+ * ...
+ * }
+ * ]
+ * @return - List of YARN applications
+ */
+ @GET
+ @Path("recommended")
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<AppStoreEntry> get() {
+ AppCatalogSolrClient sc = new AppCatalogSolrClient();
+ return sc.getRecommendedApps();
+ }
+
+ /**
+ * Search for yarn applications from solr.
+ *
+ * @apiGroup AppStoreController
+ * @apiName search
+ * @api {get} /app_store/search Find application from appstore.
+ * @apiParam {String} q Keyword to search.
+ * @apiSuccess {Object} AppStoreEntry List of matched applications.
+ * @apiSuccessExample {json} Success-Response:
+ * HTTP/1.1 200 OK
+ * [
+ * {
+ * "id":"96b7833a-e3",
+ * "org":"Hortonworks",
+ * "name":"LAMP",
+ * "desc":"Linux Apache MySQL PHP web application",
+ * "icon":"/css/img/feather.png",
+ * "like":0,
+ * "download":0,
+ * "app":null
+ * },
+ * {
+ * ...
+ * }
+ * ]
+ * @param keyword - search for keyword
+ * @return - List of YARN applications matching keyword search.
+ */
+ @GET
+ @Path("search")
+ @Produces(MediaType.APPLICATION_JSON)
+ public List<AppStoreEntry> search(@QueryParam("q") String keyword) {
+ AppCatalogSolrClient sc = new AppCatalogSolrClient();
+ return sc.search(keyword);
+ }
+
+ /**
+ * Find yarn application from solr.
+ *
+ * @param id Application ID
+ * @return AppEntry
+ */
+ @GET
+ @Path("get/{id}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public AppStoreEntry get(@PathParam("id") String id) {
+ AppCatalogSolrClient sc = new AppCatalogSolrClient();
+ return sc.findAppStoreEntry(id);
+ }
+
+ /**
+ * Register an application.
+ *
+ * @apiGroup AppStoreController
+ * @apiName register
+ * @api {post} /app_store/register Register an application in appstore.
+ * @apiParam {Object} app Application definition.
+ * @apiParamExample {json} Request-Example:
+ * {
+ * "name": "Jenkins",
+ * "organization": "Jenkins-ci.org",
+ * "description": "The leading open source automation server",
+ * "icon": "/css/img/jenkins.png",
+ * "lifetime": "3600",
+ * "components": [
+ * {
+ * "name": "jenkins",
+ * "number_of_containers": 1,
+ * "artifact": {
+ * "id": "eyang-1.openstacklocal:5000/jenkins:latest",
+ * "type": "DOCKER"
+ * },
+ * "launch_command": "",
+ * "resource": {
+ * "cpus": 1,
+ * "memory": "2048"
+ * },
+ * "configuration": {
+ * "env": {
+ * },
+ * "files": [
+ * ]
+ * }
+ * }
+ * ],
+ * "quicklinks": {
+ * "Jenkins UI": "http://jenkins.${SERVICE_NAME}.${USER}.${DOMAIN}:8080/"
+ * }
+ * }
+ * @apiSuccess {String} Response Application register result.
+ * @apiError BadRequest Error in process application registration.
+ * @param app - Yarnfile in JSON form
+ * @return Web response
+ */
+ @POST
+ @Path("register")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response register(Application app) {
+ try {
+ if (app.getName()==null) {
+ throw new IOException("Application name can not be empty.");
+ }
+ if (app.getOrganization()==null) {
+ throw new IOException("Application organization can not be empty.");
+ }
+ if (app.getDescription()==null) {
+ throw new IOException("Application description can not be empty.");
+ }
+ AppCatalogSolrClient sc = new AppCatalogSolrClient();
+ sc.register(app);
+ } catch (IOException e) {
+ return Response.status(Status.BAD_REQUEST).entity(e.getMessage()).build();
+ }
+ return Response.status(Status.ACCEPTED).build();
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/package-info.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/package-info.java
new file mode 100644
index 0000000..f2507b1
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/controller/package-info.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+/**
+ * Application Catalog controllers.
+ */
+package org.apache.hadoop.yarn.appcatalog.controller;
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/AppDetails.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/AppDetails.java
new file mode 100644
index 0000000..42012cf
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/AppDetails.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.yarn.appcatalog.model;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * Data model for user defined application configuration.
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
+public class AppDetails {
+ private String image;
+ private String version;
+ private String[] ports;
+ private String[] volumes;
+ private String[] env;
+
+ public String getImage() {
+ return image;
+ }
+
+ public void setImage(String image) {
+ this.image = image;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String[] getPorts() {
+ return ports;
+ }
+
+ public void setPorts(String[] ports2) {
+ this.ports = ports2;
+ }
+
+ public String[] getVolumes() {
+ return volumes;
+ }
+
+ public void setVolumes(String[] volumes) {
+ this.volumes = volumes;
+ }
+
+ public String[] getEnv() {
+ return env;
+ }
+
+ public void setEnv(String[] env) {
+ this.env = env;
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/AppEntry.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/AppEntry.java
new file mode 100644
index 0000000..acba4d9
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/AppEntry.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.hadoop.yarn.appcatalog.model;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import org.apache.hadoop.yarn.service.api.records.Service;
+
+/**
+ * Data model for deployed application.
+ *
+ */
+@XmlType(namespace="http://hadoop.apache.org", name="AppEntry")
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
+public class AppEntry {
+ private String id;
+ private String name;
+ private String app;
+ private Service yarnfile;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getApp() {
+ return app;
+ }
+
+ public void setApp(String app) {
+ this.app = app;
+ }
+
+ public Service getYarnfile() {
+ return yarnfile;
+ }
+
+ public void setYarnfile(Service yarnfile) {
+ this.yarnfile = yarnfile;
+ }
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/AppStoreEntry.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/AppStoreEntry.java
new file mode 100644
index 0000000..f245297
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/AppStoreEntry.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.hadoop.yarn.appcatalog.model;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.apache.hadoop.yarn.service.api.records.Service;
+
+/**
+ * Data model of application template stored in application catalog.
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
+public class AppStoreEntry {
+ private String id;
+ private String org;
+ private String name;
+ private String desc;
+ private String icon = "/css/img/feather.png";
+ private long like;
+ private long download;
+ private Service app;
+
+ public String getOrg() {
+ return org;
+ }
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDesc() {
+ return desc;
+ }
+
+ public void setDesc(String desc) {
+ this.desc = desc;
+ }
+
+ public long getLike() {
+ return like;
+ }
+
+ public void setLike(long like) {
+ this.like = like;
+ }
+
+ public long getDownload() {
+ return download;
+ }
+
+ public void setDownload(long download) {
+ this.download = download;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public Service getApp() {
+ return app;
+ }
+
+ public void setApp(Service app) {
+ this.app = app;
+ }
+
+ public String getIcon() {
+ return icon;
+ }
+
+ public void setIcon(String icon) {
+ this.icon = icon;
+ }
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/Application.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/Application.java
new file mode 100644
index 0000000..dbc31b1
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/Application.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.hadoop.yarn.appcatalog.model;
+
+import javax.xml.bind.annotation.XmlRootElement;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyOrder;
+import org.apache.hadoop.yarn.service.api.records.Service;
+
+/**
+ * Data model of display recommended applications and descriptions.
+ */
+@XmlRootElement
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonPropertyOrder({ "organization", "name", "description", "icon" })
+public class Application extends Service {
+ private static final long serialVersionUID = -1776203219305414248L;
+
+ private String organization;
+ private String description;
+ private String icon;
+
+ @JsonProperty("organization")
+ public String getOrganization() {
+ return organization;
+ }
+ public void setOrganization(String organization) {
+ this.organization = organization;
+ }
+
+ @JsonProperty("description")
+ public String getDescription() {
+ return description;
+ }
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @JsonProperty("icon")
+ public String getIcon() {
+ return icon;
+ }
+ public void setIcon(String icon) {
+ this.icon = icon;
+ }
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/package-info.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/package-info.java
new file mode 100644
index 0000000..9a20b99
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/model/package-info.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+/**
+ * Application Catalog Data Models.
+ */
+package org.apache.hadoop.yarn.appcatalog.model;
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/utils/RandomWord.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/utils/RandomWord.java
new file mode 100644
index 0000000..4346d1e
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/utils/RandomWord.java
@@ -0,0 +1,422 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.hadoop.yarn.appcatalog.utils;
+
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * Random word generator utility.
+ */
+public final class RandomWord {
+
+ private RandomWord() {
+ }
+
+ public static String getNewWord(int wordLength) throws WordLengthException {
+ if (wordLength < 3 || wordLength > 15) {
+ throw new WordLengthException(
+ "Word length error, words must be between 3 and 15 characters long.");
+ }
+ String theNewWord = "e";
+ while (theNewWord.equals("e")) {
+ try {
+ theNewWord = generateRandomWord(wordLength);
+ } catch (Exception e) {
+ }
+ }
+ return theNewWord;
+ }
+
+ private static String generateRandomWord(int wordLength) {
+ String randomWord;
+ // -----------------------------------------------------------------------
+ // Bigram source and general concept based on
+ // https://github.com/scrollback/scrollback & described in
+ // https://www.hackerearth.com/notes/random-pronouncable-text-generator/
+ String[] startBiGram = {"TH", "OF", "AN", "IN", "TO", "CO", "BE", "HE",
+ "RE", "HA", "WA", "FO", "WH", "MA", "WI", "ON", "HI", "PR", "ST", "NO",
+ "IS", "IT", "SE", "WE", "AS", "CA", "DE", "SO", "MO", "SH", "DI", "AL",
+ "AR", "LI", "WO", "FR", "PA", "ME", "AT", "SU", "BU", "SA", "FI", "NE",
+ "CH", "PO", "HO", "DO", "OR", "UN", "LO", "EX", "BY", "FA", "LA", "LE",
+ "PE", "MI", "SI", "YO", "TR", "BA", "GO", "BO", "GR", "TE", "EN", "OU",
+ "RA", "AC", "FE", "PL", "CL", "SP", "BR", "EV", "TA", "DA", "AB", "TI",
+ "RO", "MU", "EA", "NA", "SC", "AD", "GE", "YE", "AF", "AG", "UP", "AP",
+ "DR", "US", "PU", "CE", "IF", "RI", "VI", "IM", "AM", "KN", "OP", "CR",
+ "OT", "JU", "QU", "TW", "GA", "VA", "VE", "PI", "GI", "BI", "FL", "BL",
+ "EL", "JO", "FU", "HU", "CU", "RU", "OV", "MY", "OB", "KE", "EF", "PH",
+ "CI", "KI", "NI", "SL", "EM", "SM", "VO", "MR", "WR", "ES", "DU", "TU",
+ "AU", "NU", "GU", "OW", "SY", "JA", "OC", "EC", "ED", "ID", "JE", "AI",
+ "EI", "SK", "OL", "GL", "EQ", "LU", "AV", "SW", "AW", "EY", "TY"};
+ String[] lookupBiGram = {"TH", "AN", "IN", "IO", "EN", "TI", "FO", "HE",
+ "HA", "HI", "TE", "AT", "ER", "AL", "WA", "VE", "CO", "RE", "IT", "WI",
+ "ME", "NC", "ON", "PR", "AR", "ES", "EV", "ST", "EA", "IV", "EC", "NO",
+ "OU", "PE", "IL", "IS", "MA", "AV", "OM", "IC", "GH", "DE", "AI", "CT",
+ "IG", "ID", " OR", "OV", "UL", "YO", "BU", "RA", "FR", "RO", "WH", "OT",
+ "BL", "NT", "UN", "TR", "HO", "AC", "TU", "WE", "CA", "SH", "UR", "IE",
+ "PA", "TO", "EE", "LI", "RI", "UG", "AM", "ND", "US", "LL", "AS", "TA",
+ "LE", "MO", "WO", "MI", "AB", "EL", "IA", "NA", "SS", "AG", "TT", "NE",
+ "PL", " LA", "OS", "CE", "DI", "BE", "AP", "SI", "NI", "OW", "SO", "AK",
+ "CH", "EM", "IM", "SE", "NS", "PO", "EI", "EX", "KI", "UC", "AD", "GR",
+ "IR", "NG", "OP", "SP", "OL", "DA", "NL", "TL", "LO", "BO", "RS", "FE",
+ "FI", "SU", "GE", "MP", "UA", "OO", "RT", "SA", "CR", "FF", "IK", "MB",
+ " KE", "FA", "CI", "EQ", "AF", "ET", "AY", "MU", "UE", "HR", "TW", "GI",
+ "OI", "VI", "CU", "FU", "ED", "QU", "UT", "RC", "OF", "CL", "FT", "IZ",
+ "PP", "RG", "DU", "RM", "YE", "RL", "DO", "AU", "EP", "BA", "JU", "RD",
+ "RU", "OG", "BR", "EF", "KN", "LS", "GA", "PI", "YI", "BI", "IB", "UB",
+ "VA", "OC", "IF", "RN", "RR", "SC", "TC", "CK", "DG", "DR", "MM", "NN",
+ "OD", "RV", "UD", "XP", "JE", "UM", "EG", "DL", "PH", "SL", "GO", "CC",
+ "LU", "OA", "PU", "UI", "YS", "ZA", "HU", "MR", "OE", "SY", "EO", "TY",
+ "UP", "FL", "LM", "NF", "RP", "OH", "NU", "XA", "OB", "VO", "DM", "GN",
+ " LD", "PT", "SK", "WR", "JO", "LT", "YT", "UF", "BJ", "DD", "EY", "GG",
+ "GL", "GU", "HT", "LV", "MS", "NM", "NV", "OK", "PM", "RK", "SW", "TM",
+ "XC", "ZE", "AW", "SM"};
+ String[][][] nextCharLookup = {
+ {{"E", "A", "I", "O", "R"}, {"E", "O"}},
+ {{"D", "T", "Y", "C", "S", "G", "N", "I", "O", "E", "A", "K"},
+ {"D", "T", "Y", "S", "G", "O", "E", "A", "K"}},
+ {{"G", "T", "E", "D", "S", "C", "A", "I", "K", "V", "U", "N", "F"},
+ {"G", "T", "E", "D", "S", "A", "K"}},
+ {{"N", "U", "R"}, {"N", "U", "R"}},
+ {{"T", "C", "D", "S", "E", "I", "G", "O", "N", "A"},
+ {"T", "D", "S", "E", "G", "O", "A"}},
+ {{"O", "N", "C", "V", "M", "L", "E", "T", "S", "A", "R", "F"},
+ {"N", "C", "M", "L", "E", "T", "S", "A", "R", "F"}},
+ {{"R", "U", "O", "L"}, {"R", "U", "O", "L"}},
+ {{"R", "N", "Y", "S", "M", "I", "A", "L", "D", "T"},
+ {"R", "N", "Y", "S", "M", "A", "L", "D", "T"}},
+ {{"T", "D", "V", "N", "S", "R", "P", "L"},
+ {"T", "D", "N", "S", "R", "L"}},
+ {{"S", "N", "C", "M", "L", "P", "G", "T", "R", "E"},
+ {"S", "N", "C", "M", "L", "P", "G", "T", "R", "E"}},
+ {{"R", "D", "N", "S", "M", "L", "E", "C", "A"},
+ {"R", "D", "N", "S", "M", "L", "E", "A"}},
+ {{"I", "E", "T", "H", "U", "O", "C"}, {"E", "H", "O"}},
+ {{"E", "S", "I", "A", "N", "Y", "T", "V", "M", "R", "O", "L", "G",
+ "F", "C"}, {"E", "S", "A", "N", "Y", "T", "M"}},
+ {{"L", "S", "I", "T", "E", "U", "O", "M", "K", "F", "A"},
+ {"L", "S", "T", "E", "F"}},
+ {{"S", "Y", "R", "T", "N", "L"}, {"S", "Y", "R", "T", "N", "L"}},
+ {{"R", "N", "L", "S", "D"}, {"R", "N", "L", "S", "D"}},
+ {{"N", "M", "U", "R", "L", "V", "S", "O"},
+ {"N", "M", "U", "R", "L", "O"}},
+ {{"S", "A", "D", "N", "E", "C", "L", "T", "P", "M", "V", "G", "F",
+ "Q"}, {"S", "A", "D", "N", "E", "L", "T", "P", "M"}},
+ {{"H", "I", "Y", "E", "S", "T", "A", "U"},
+ {"H", "Y", "E", "S", "A"}},
+ {{"T", "L", "N", "S"}, {"T", "L", "N", "S"}},
+ {{"N", "R", "D", "T", "S", "M", "A"},
+ {"N", "R", "D", "T", "S", "M", "A"}},
+ {{"E", "I", "H", "T", "R", "O", "L"}, {"E", "H", "T"}},
+ {{"S", "E", "T", "G", "A", "D", "L", "C", "V", "O", "I", "F"},
+ {"S", "E", "T", "G", "A", "D", "O"}},
+ {{"O", "E", "I", "A"}, {"E", "A"}},
+ {{"E", "T", "D", "Y", "S", "I", "R", "L", "M", "K", "G", "A", "O",
+ "N", "C"}, {"E", "T", "D", "Y", "S", "M", "K", "A", "N"}},
+ {{"S", "T", "E", "I", "P", "U", "C"}, {"S", "T", "E"}},
+ {{"E", "I"}, {"E"}},
+ {{"A", "R", "I", "E", "O", "U", "S"}, {"A", "E", "O", "S"}},
+ {{"R", "S", "T", "D", "L", "C", "N", "V", "M", "K"},
+ {"R", "S", "T", "D", "L", "N", "M"}},
+ {{"E", "I", "A"}, {"E"}},
+ {{"T", "O", "I", "E", "A", "U", "R", "H"}, {"T", "E", "H"}},
+ {{"T", "W", "R", "U", "N", "M"}, {"T", "W", "R", "U", "N", "M"}},
+ {{"T", "L", "R", "N", "S", "G", "P", "B"},
+ {"T", "L", "R", "N", "S", "P"}},
+ {{"R", "N", "C", "A", "D", "T", "O"}, {"R", "N", "A", "D", "T"}},
+ {{"L", "E", "I", "Y", "D", "A"}, {"L", "E", "Y", "D"}},
+ {{"T", "H", "S", "I", "E", "C", "M"}, {"T", "H", "S", "E", "M"}},
+ {{"N", "T", "L", "K", "D", "S", "I", "G"},
+ {"N", "T", "L", "D", "S"}},
+ {{"E", "I", "A"}, {"E"}},
+ {{"E", "P", "M", "I", "A"}, {"E"}},
+ {{"A", "H", "E", "I", "T", "K", "U", "S"},
+ {"H", "E", "T", "K", "S"}},
+ {{"T"}, {"T"}},
+ {{"R", "N", "S", "D", "A", "V", "P", "T", "M", "L", "F"},
+ {"R", "N", "S", "D", "A", "P", "T", "M", "L"}},
+ {{"N", "D", "R", "L", "T"}, {"N", "D", "R", "L", "T"}},
+ {{"I", "E", "U", "S", "O"}, {"E", "S", "O"}},
+ {{"H", "N", "I"}, {"H", "N"}}, {{"E"}, {"E"}},
+ {{"E", "T", "M", "D", "S", "K", "I", "Y", "L", "G", "A", "R", "N",
+ "C"}, {"E", "T", "M", "D", "S", "K", "Y", "A", "N"}},
+ {{"E", "I"}, {"E"}},
+ {{"D", "T", "A", "L"}, {"D", "T", "L"}}, {{"U"}, {"U"}},
+ {{"T", "S", "R", "I"}, {"T", "S", "R"}},
+ {{"T", "N", "L", "C", "I", "M", "D", "S", "R", "P", "G", "B"},
+ {"T", "N", "L", "M", "D", "S", "R"}},
+ {{"O", "E", "A"}, {"E", "A"}},
+ {{"M", "U", "V", "P", "N", "W", "S", "O", "L", "D", "C", "B", "A",
+ "T", "G"}, {"M", "U", "P", "N", "W", "O", "L", "D", "T"}},
+ {{"I", "E", "O", "A"}, {"E", "O"}},
+ {{"H", "E", "T", "I"}, {"H", "E"}},
+ {{"E", "I", "Y", "O", "A"}, {"E", "Y"}},
+ {{"E", "I", "S", "R", "O", "A", "L", "Y", "U", "H"},
+ {"E", "S", "O", "A", "Y", "H"}},
+ {{"D", "T", "I", "C", "G"}, {"D", "T", "G"}},
+ {{"A", "I", "O", "E", "U", "Y"}, {"A", "E", "Y"}},
+ {{"U", "W", "S", "R", "L", "O", "M", "T", "P", "N", "D"},
+ {"U", "W", "R", "L", "O", "M", "T", "P", "N", "D"}},
+ {{"T", "E", "K", "H", "C", "R", "I"}, {"T", "E", "K", "H"}},
+ {{"R", "D", "A", "T"}, {"R", "T"}},
+ {{"R", "L", "E", "V", "S", "N", "A"},
+ {"R", "L", "E", "S", "N", "A"}},
+ {{"L", "N", "T", "R", "U", "S", "M", "P"},
+ {"L", "N", "T", "R", "S", "M"}},
+ {{"E", "O", "I", "A"}, {"E", "O"}},
+ {{"E", "N", "T", "S", "I", "A", "Y", "R", "P", "C"},
+ {"E", "N", "T", "S", "A", "Y"}},
+ {{"S", "N", "D", "T", "W", "V", "R", "L", "F"},
+ {"S", "N", "D", "T", "W", "R", "L"}},
+ {{"R", "T", "S", "N", "L", "I", "C"}, {"R", "T", "S", "N", "L"}},
+ {{"R", "O", "N", "W", "P", "M", "L"},
+ {"R", "O", "N", "W", "P", "M", "L"}},
+ {{"N", "D", "T", "M", "S", "R", "P", "L", "K"},
+ {"N", "D", "T", "M", "S", "R", "P", "L", "K"}},
+ {{"N", "T", "S", "C", "K", "G", "E", "F", "Z", "V", "O", "M", "A"},
+ {"N", "T", "S", "C", "G", "E", "F", "M", "A"}},
+ {{"N", "E", "C", "T", "S", "G", "A", "V", "O", "P", "M", "L", "D",
+ "B"}, {"N", "E", "C", "T", "S", "G", "A", "P", "M", "L", "D"}},
+ {{"H", "G"}, {"H"}}, {{"E", "P", "I", "O", "A"}, {"E"}},
+ {{"E", "I", "S", "A", "U", "O"}, {"E", "S", "O"}},
+ {{"E", "T", "I", "S", "L", "H"}, {"E", "T", "S", "H"}},
+ {{"Y", "E", "O", "I", "S", "A"}, {"Y", "E", "S"}},
+ {{"T", "S", "E", "I", "U", "O", "K", "H"},
+ {"T", "S", "E", "O", "H"}},
+ {{"T", "N", "L", "I", "R", "K", "B", "G", "C"},
+ {"T", "N", "L", "R"}},
+ {{"S", "D", "A", "T", "C", "R", "N", "M", "G", "V", "F"},
+ {"S", "D", "A", "T", "R", "N", "M"}},
+ {{"R", "S", "V", "T", "U", "D"}, {"R", "T", "U", "D"}},
+ {{"R", "U"}, {"R", "U"}},
+ {{"N", "L", "S", "T", "C", "G"}, {"N", "L", "S", "T", "C", "G"}},
+ {{"L", "O", "I"}, {}},
+ {{"L", "Y", "I", "E", "F", "O", "A", "T", "S", "P", "D"},
+ {"L", "Y", "E", "F", "T", "S", "D"}},
+ {{"L", "N", "T"}, {"L", "N", "T"}},
+ {{"L", "T", "R", "N", "M"}, {"L", "T", "R", "N", "M"}},
+ {{"I", "E", "U", "O", "A"}, {"E", "O"}},
+ {{"E", "A", "O"}, {"E", "O"}}, {{"E", "L", "I"}, {"E"}},
+ {{"D", "S", "W", "R", "E", "Y", "V", "T", "L", "C", "A"},
+ {"D", "S", "W", "R", "E", "Y", "T", "L", "A"}},
+ {{"A", "E", "I", "Y", "O"}, {"E", "Y"}},
+ {{"T", "N", "R", "S", "C", "Y", "W", "I", "B"},
+ {"T", "N", "R", "S", "Y", "W"}},
+ {{"T", "E", "S", "I"}, {"T", "E", "S"}},
+ {{"S", "N", "R", "D", "P", "L", "I"},
+ {"S", "N", "R", "D", "P", "L"}},
+ {{"S", "N", "T", "D", "F", "E", "C", "A", "V", "R"},
+ {"S", "N", "T", "D", "F", "E", "C", "A", "R"}},
+ {{"R", "E", "C", "T", "L", "F", "S", "I", "G", "D", "A"},
+ {"R", "E", "T", "L", "S", "D", "A"}},
+ {{"P", "E", "A"}, {"E"}},
+ {{"O", "N", "D", "T", "S", "G", "C", "B", "V", "M", "A"},
+ {"N", "D", "T", "S", "G", "C", "M", "A"}},
+ {{"N", "T", "S", "C", "Z", "O", "G", "F"},
+ {"N", "T", "S", "C", "G", "F"}},
+ {{"N", "E", "S", "I", "A"}, {"N", "E", "S"}},
+ {{"N", "M", "U", "L", "C", "R"}, {"N", "M", "U", "L", "R"}},
+ {{"E", "I"}, {"E"}},
+ {{"E", "A", "I", "O", "U", "R"}, {"E", "O"}},
+ {{"E", "S", "P", "O", "B", "A", "I"}, {"E", "S"}},
+ {{"E", "P", "I", "A", "S", "M"}, {"E", "S"}},
+ {{"D", "N", "L", "S", "R", "E", "C", "T", "V", "A"},
+ {"D", "N", "L", "S", "R", "E", "T", "A"}},
+ {{"T", "I", "E"}, {"T", "E"}},
+ {{"S", "R", "N", "L", "W", "T", "I"}, {"R", "N", "L", "W", "T"}},
+ {{"R", "N", "G", "T"}, {"R", "N", "G", "T"}},
+ {{"P", "T", "I", "C", "A"}, {"T"}}, {{"N"}, {"N"}},
+ {{"H", "T", "K", "E"}, {"H", "T", "K", "E"}},
+ {{"E", "I", "Y", "V", "M", "D"}, {"E", "Y"}},
+ {{"E", "A", "O"}, {"E", "A"}},
+ {{"E", "S", "T", "L", "I"}, {"E", "S", "T"}},
+ {{"E", "S", "L", "T", "R", "I"}, {"E", "S"}},
+ {{"E", "P", "L"}, {"E"}}, {{"E", "O", "I", "A"}, {"E"}},
+ {{"D", "L", "I", "O", "E", "U"}, {"D", "L", "E"}},
+ {{"Y", "T", "R", "N"}, {"Y", "T", "R", "N"}},
+ {{"Y"}, {"Y"}}, {{"Y", "E"}, {"Y", "E"}},
+ {{"W", "N", "O", "S", "C", "V", "U", "T", "R", "P", "G"},
+ {"W", "N", "O", "U", "T", "R", "P"}},
+ {{"U", "T", "R", "O", "D", "A"}, {"U", "T", "R", "O", "D"}},
+ {{"T", "E", "O", "I"}, {"T", "E", "O"}},
+ {{"R", "E", "W", "L", "C", "A"}, {"R", "E", "W", "L", "A"}},
+ {{"R", "N", "C", "E", "L", "G"}, {"R", "N", "C", "E", "L", "G"}},
+ {{"R", "C", "P", "B", "M", "L", "A"}, {"R", "P", "M", "L"}},
+ {{"N", "T", "S", "R", "D"}, {"N", "T", "S", "R", "D"}},
+ {{"L", "O", "A", "T", "R", "E"}, {"T", "E"}},
+ {{"L", "T", "R"}, {"L", "T", "R"}},
+ {{"K", "D", "L", "T", "R", "N", "M"},
+ {"K", "D", "L", "T", "R", "N", "M"}},
+ {{"I", "H", "A", "E", "Y", "U", "S"}, {"H", "A", "E", "Y", "S"}},
+ {{"I", "M", "Y", "N", "L"}, {"M", "Y", "N", "L"}},
+ {{"E", "I", "O", "A"}, {"E", "A"}}, {{"E", "I"}, {"E"}},
+ {{"E"}, {"E"}}, {{"E"}, {"E"}},
+ {{"D", "N", "T", "S", "R", "E"}, {"D", "N", "T", "S", "R", "E"}},
+ {{"C", "R", "M", "I"}, {"R", "M"}},
+ {{"A", "T", "E", "S", "P", "N"}, {"A", "T", "E", "S", "P", "N"}},
+ {{"U"}, {}}, {{"T", "F"}, {"T", "F"}},
+ {{"T", "I", "H", "E", "Y", "W", "S", "A"},
+ {"H", "E", "Y", "S", "A"}},
+ {{"S", "E"}, {"S"}},
+ {{"S", "N", "L", "C"}, {"S", "N", "L"}},
+ {{"S", "N"}, {"S", "N"}}, {{"O", "E", "I"}, {"E"}},
+ {{"O", "E"}, {"O", "E"}},
+ {{"N", "V", "O", "C"}, {"N", "C"}}, {{"N"}, {"N"}},
+ {{"N", "D", "S", "C", "T", "O", "L", "E"},
+ {"N", "D", "S", "C", "T", "L", "E"}},
+ {{"L", "R", "T", "S"}, {"L", "R", "T", "S"}},
+ {{"L", "R", "N"}, {"L", "R", "N"}},
+ {{"I", "U", "E"}, {"E"}}, {{"I", "E", "A"}, {"E"}},
+ {{"I", "H", "E"}, {"H", "E"}}, {{"H", "E"}, {"H", "E"}},
+ {{"F", "T"}, {"F", "T"}}, {{"E", "A", "U", "O"}, {"E"}},
+ {{"E"}, {"E"}}, {{"E", "A"}, {"E"}},
+ {{"E", "O", "R", "L"}, {"E"}}, {{"E", "A"}, {"E"}},
+ {{"C", "S", "R", "A"}, {"S", "R"}},
+ {{"A", "S", "I", "E"}, {"S", "E"}},
+ {{"A", "S", "D"}, {"A", "S", "D"}},
+ {{"Y", "D"}, {"Y", "D"}},
+ {{"W", "N", "M", "E"}, {"W", "N", "M"}},
+ {{"T", "S"}, {"T", "S"}},
+ {{"T", "O", "E", "A"}, {"T", "E"}},
+ {{"S", "C", "R", "N", "L"}, {"S", "R", "N", "L"}},
+ {{"S"}, {"S"}}, {{"S", "E", "I"}, {"S", "E"}},
+ {{"S", "N", "C"}, {"S", "N"}}, {{"R", "I"}, {}},
+ {{"O", "I", "E", "A"}, {"E", "A"}},
+ {{"O", "F", "U", "T", "E"}, {"F", "T", "E"}},
+ {{"O", "E"}, {"O", "E"}}, {{"O"}, {"O"}},
+ {{"N", "I", "T", "R"}, {"N", "T", "R"}},
+ {{"N", "T", "R", "E", "C"}, {"N", "T", "R", "E", "C"}},
+ {{"N"}, {"N"}}, {{"L", "T", "N"}, {"L", "T", "N"}},
+ {{"L", "I", "E"}, {"E"}}, {{"L"}, {}},
+ {{"L", "T", "R", "N"}, {"L", "T", "R", "N"}},
+ {{"K", "I", "E", "C", "A"}, {"K", "E"}},
+ {{"I", "F", "E", "T"}, {"F", "E", "T"}},
+ {{"I", "E", "M", "A"}, {"E", "A"}},
+ {{"I", "E", "Y", "O"}, {"E", "Y"}},
+ {{"H", "R", "O", "I", "A"}, {"H"}}, {{"H"}, {"H"}},
+ {{"E"}, {"E"}}, {{"E"}, {"E"}},
+ {{"E", "O", "I", "A"}, {"E", "A"}},
+ {{"E", "U", "I"}, {"E"}}, {{"E", "O", "I"}, {"E", "O"}},
+ {{"E", "Y", "U"}, {"E", "Y"}}, {{"E", "I"}, {"E"}},
+ {{"E", "I"}, {"E"}}, {{"E"}, {"E"}}, {{"C"}, {}},
+ {{"B", "E"}, {"E"}}, {{"A", "R", "I", "E"}, {"E"}},
+ {{"Y", "E"}, {"Y", "E"}},
+ {{"Y", "O", "I", "E"}, {"Y", "O", "E"}},
+ {{"Y", "A"}, {"Y"}}, {{"V", "T", "O"}, {"T", "O"}},
+ {{"U", "O", "E"}, {"E"}},
+ {{"T", "S", "M", "E", "D"}, {"T", "S", "M", "E"}},
+ {{"T", "R", "D"}, {"T", "R", "D"}},
+ {{"T", "R", "L", "B"}, {"T", "R", "L"}},
+ {{"T", "R", "L"}, {"T", "R", "L"}}, {{"T"}, {"T"}},
+ {{"T"}, {"T"}},
+ {{"S", "R", "N", "M"}, {"S", "R", "N", "M"}},
+ {{"S"}, {"S"}}, {{"S"}, {"S"}}, {{"S"}, {"S"}},
+ {{"R", "P"}, {"R", "P"}}, {{"P"}, {}}, {{"P", "O"}, {}},
+ {{"O", "E"}, {"E"}}, {{"O"}, {}}, {{"O"}, {}},
+ {{"O"}, {}}, {{"N"}, {}}, {{"M"}, {"M"}},
+ {{"M"}, {"M"}}, {{"L"}, {}}, {{"L"}, {"L"}},
+ {{"I"}, {}}, {{"I"}, {}}, {{"I", "E"}, {"E"}},
+ {{"I"}, {}}, {{"I", "E"}, {"E"}}, {{"I"}, {}},
+ {{"H"}, {}}, {{"H", "E"}, {"H", "E"}}, {{"H"}, {"H"}},
+ {{"F"}, {"F"}}, {{"E"}, {}}, {{"E"}, {"E"}},
+ {{"E"}, {}}, {{"E"}, {"E"}}, {{"E", "A"}, {"E"}},
+ {{"E"}, {"E"}}, {{"E"}, {"E"}}, {{"E"}, {"E"}},
+ {{"E"}, {"E"}}, {{"E"}, {"E"}}, {{"E"}, {"E"}},
+ {{"E"}, {"E"}}, {{"E"}, {"E"}}, {{"E"}, {"E"}},
+ {{"E"}, {"E"}}, {{"E"}, {"E"}}, {{"E"}, {"E"}},
+ {{"D"}, {"D"}}, {{"A"}, {}}, {{"A"}, {}}};
+ // ------------------------------------------------------------------------
+ randomWord = startBiGram[indexGenerator(startBiGram.length)];
+ int flag = 0;
+ int count = 0;
+ String previousWord;
+ while (randomWord.length() != wordLength) {
+ previousWord = randomWord;
+ randomWord = addCharacter(startBiGram, wordLength, randomWord,
+ lookupBiGram, nextCharLookup, flag);
+ if (previousWord.equals(randomWord)) {
+ count++;
+ } else {
+ flag = 0;
+ }
+ if (count == 5) {
+ flag = 1;
+ count++;
+ } else if (count == 20) {
+ randomWord = startBiGram[indexGenerator(startBiGram.length)];
+ count = 0;
+ }
+ }
+ return randomWord;
+ }
+
+ private static String addCharacter(String[] startBiGram, int desiredLength,
+ String currentWord, String[] lookupBiGram, String[][][] nextCharLookup,
+ int flag) {
+ int mainIndex = getLookupIndex(currentWord, lookupBiGram);
+ int type = 0;
+ if (currentWord.length() == (desiredLength - 1)) {
+ type = 1;
+ }
+ while (mainIndex < 0 || mainIndex > 263
+ || nextCharLookup[mainIndex][type].length <= 0) {
+ if (currentWord.length() == 2) {
+ return startBiGram[indexGenerator(startBiGram.length)];
+ }
+ if (flag == 1) {
+ currentWord = backtrack(currentWord, 2);
+ flag = 0;
+ } else {
+ currentWord = backtrack(currentWord, 1);
+ }
+ mainIndex = getLookupIndex(currentWord, lookupBiGram);
+ if (type == 1) {
+ type = 0;
+ }
+ }
+ String updatedWord = currentWord
+ + getNextCharacter(type, mainIndex, nextCharLookup);
+ return updatedWord;
+ }
+
+ private static int indexGenerator(int arrayLength) {
+ int theIndex;
+ Random generator = new Random();
+ theIndex = generator.nextInt(arrayLength);
+ return theIndex;
+ }
+
+ private static String getNextCharacter(int type, int mainIndex,
+ String[][][] theCharacterVault) {
+ String nextChar;
+ int i = indexGenerator(theCharacterVault[mainIndex][type].length);
+ nextChar = theCharacterVault[mainIndex][type][i];
+ return nextChar;
+ }
+
+ private static String backtrack(String theWord, int numberChars) {
+ theWord = theWord.substring(0, theWord.length() - numberChars);
+ return theWord;
+ }
+
+ private static int getLookupIndex(String theWord, String[] lookupArray) {
+ String lookupCharacters = theWord.substring(theWord.length() - 2);
+ int lookupIndex = Arrays.asList(lookupArray).indexOf(lookupCharacters);
+ return lookupIndex;
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/utils/WordLengthException.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/utils/WordLengthException.java
new file mode 100644
index 0000000..42a5f7f
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/utils/WordLengthException.java
@@ -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.
+ */
+package org.apache.hadoop.yarn.appcatalog.utils;
+
+/**
+ * Word length exception class.
+ */
+public class WordLengthException extends Exception {
+ private static final long serialVersionUID = -217354336577036950L;
+
+ public WordLengthException(String message){
+ super(message);
+ }
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/utils/package-info.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/utils/package-info.java
new file mode 100644
index 0000000..bde7fc5
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/java/org/apache/hadoop/yarn/appcatalog/utils/package-info.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+/**
+ * Application Catalog Utilities classes.
+ */
+package org.apache.hadoop.yarn.appcatalog.utils;
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/app.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/app.js
new file mode 100644
index 0000000..0a7b6db
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/app.js
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var app = angular.module('app', [
+ 'ngRoute',
+ 'filters',
+ 'controllers'
+]);
+
+app.directive('jsonText', function() {
+ return {
+ restrict: 'A',
+ require: 'ngModel',
+ link: function(scope, element, attr, ngModel) {
+ function into(input) {
+ console.log(JSON.parse(input));
+ return JSON.parse(input);
+ }
+ function out(data) {
+ return JSON.stringify(data);
+ }
+ ngModel.$parsers.push(into);
+ ngModel.$formatters.push(out);
+ }
+ };
+});
+
+app.config(['$routeProvider',
+ function ($routeProvider) {
+ $routeProvider.when('/', {
+ templateUrl: 'partials/home.html',
+ controller: 'AppStoreController'
+ }).when('/app/:id', {
+ templateUrl: 'partials/details.html',
+ controller: 'AppDetailsController'
+ }).when('/new', {
+ templateUrl: 'partials/new.html',
+ controller: 'NewAppController'
+ }).when('/deploy/:id', {
+ templateUrl: 'partials/deploy.html',
+ controller: 'DeployAppController'
+ }).otherwise({
+ redirectTo: '/'
+ });
+}]);
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/controllers.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/controllers.js
new file mode 100644
index 0000000..241f15d
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/controllers.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.
+ */
+var controllers = angular.module("controllers", []);
+
+controllers.controller("AppListController", [ '$scope', '$rootScope', '$http',
+ function($scope, $rootScope, $http) {
+ $scope.appList = [];
+
+ function successCallback(response) {
+ $scope.appList = response.data;
+ $rootScope.$emit("hideLoadScreen", {});
+ }
+
+ function errorCallback(response) {
+ $rootScope.$emit("hideLoadScreen", {});
+ console.log("Error in downloading application list");
+ }
+
+ $rootScope.$on("RefreshAppList", function() {
+ $scope.refreshList();
+ });
+
+ $scope.refreshList = function() {
+ $http({
+ method : 'GET',
+ url : '/v1/app_list'
+ }).then(successCallback, errorCallback);
+ }
+
+ $scope.deleteApp = function(id, name) {
+ $rootScope.$emit("showLoadScreen", {});
+ $http({
+ method: 'DELETE',
+ url: '/v1/app_list/' + id + '/' + name
+ }).then(function(response) {
+ $rootScope.$emit("RefreshAppList", {});
+ window.location = '/#';
+ }, function(response) {
+ console.log(response);
+ });
+ }
+ $http({
+ method : 'GET',
+ url : '/v1/app_list'
+ }).then(successCallback, errorCallback);
+ } ]);
+
+controllers.controller("AppStoreController", [ '$scope', '$rootScope', '$http',
+ function($scope, $rootScope, $http) {
+ $scope.canDeployApp = function() {
+ return false;
+ };
+ $scope.appStore = [];
+ $scope.searchText = null;
+
+ function successCallback(response) {
+ $scope.appStore = response.data;
+ }
+
+ function errorCallback(response) {
+ console.log("Error in downloading AppStore information.");
+ }
+
+ $scope.deployApp = function(id) {
+ window.location = '/#!/deploy/' + id;
+ }
+
+ $http({
+ method : 'GET',
+ url : '/v1/app_store/recommended'
+ }).then(successCallback, errorCallback);
+
+ $scope.change = function(text) {
+ var q = $scope.searchText;
+ $http({
+ method : 'GET',
+ url : '/v1/app_store/search?q=' + q
+ }).then(successCallback, errorCallback);
+ }
+ } ]);
+
+controllers.controller("AppDetailsController", [ '$scope', '$interval', '$rootScope', '$http',
+ '$routeParams', function($scope, $interval, $rootScope, $http, $routeParams) {
+ $scope.details = {"yarnfile":{"state":"UNKNOWN"}};
+ $scope.appName = $routeParams.id;
+ var timer = $interval(function() {
+ $scope.refreshAppDetails();
+ }, 2000);
+
+ $scope.refreshAppDetails = function() {
+ $http({
+ method : 'GET',
+ url : '/v1/app_details/status/' + $scope.appName
+ }).then(successCallback, errorCallback);
+ }
+
+ $scope.stopApp = function(id) {
+ $http({
+ method : 'POST',
+ url : '/v1/app_details/stop/' + id
+ }).then(function(data, status, header, config) {
+ $scope.refreshAppDetails();
+ }, errorCallback);
+ }
+
+ $scope.restartApp = function(id) {
+ $http({
+ method : 'POST',
+ url : '/v1/app_details/restart/' + id
+ }).then(function(data, status, header, config) {
+ $scope.refreshAppDetails();
+ }, errorCallback);
+ }
+
+ $scope.canDeployApp = function() {
+ return true;
+ };
+
+ $scope.checkServiceLink = function() {
+ if ($scope.details.yarnfile.state != "STABLE") {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function successCallback(response) {
+ if (response.data.yarnfile.components.length!=0) {
+ $scope.details = response.data;
+ } else {
+ // When application is in accepted or failed state, it does not
+ // have components detail, hence we update states only.
+ $scope.details.yarnfile.state = response.data.yarnfile.state;
+ }
+ }
+
+ function errorCallback(response) {
+ console.log("Error in getting application detail");
+ }
+
+ $rootScope.$on("RefreshAppDetails", function() {
+ $scope.refreshAppDetails();
+ });
+
+ $scope.$on("$locationChangeStart", function() {
+ $interval.cancel(timer);
+ });
+
+ $scope.$on('$destroy', function() {
+ $interval.cancel(timer);
+ });
+
+ $http({
+ method : 'GET',
+ url : '/v1/app_details/config/' + $scope.appName
+ }).then(successCallback, errorCallback);
+
+ } ]);
+
+controllers.controller("NewAppController", [ '$scope', '$rootScope', '$http', function($scope, $rootScope, $http) {
+ $scope.details = {
+ "name" : "",
+ "version" : "",
+ "organization" : "",
+ "description" : "",
+ "quicklinks": {
+ "UI": "http://${SERVICE_NAME}.${USER}.${DOMAIN}:8080/"
+ },
+ "icon" : "",
+ "components" : [
+ {
+ "name" : "",
+ "number_of_containers" : 1,
+ "artifact" : {
+ "id": "centos:latest"
+ },
+ "launch_command": "",
+ "resource" : {
+ "cpus" : 1,
+ "memory" : 2048
+ },
+ "run_privileged_container" : false,
+ "dependencies" : [],
+ "placement_policy" : {
+ "constraints" : []
+ },
+ "configuration" : {
+ "env" : {
+ "YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE":"true"
+ },
+ "properties" : {
+ "docker.network":"host"
+ }
+ }
+ }
+ ]
+ };
+
+ $scope.template = {
+ "name" : "",
+ "number_of_containers" : 1,
+ "artifact" : {
+ "id": "centos:latest"
+ },
+ "launch_command": "",
+ "resource" : {
+ "cpus" : 1,
+ "memory" : 2048
+ },
+ "run_privileged_container" : false,
+ "dependencies" : [],
+ "placement_policy" : {
+ "constraints" : []
+ },
+ "configuration" : {
+ "env" : {
+ "YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE":"true"
+ },
+ "properties" : {
+ "docker.network":"host"
+ }
+ }
+ };
+
+ $scope.message = null;
+ $scope.error = null;
+
+ $scope.save = function() {
+ $http({
+ method : 'POST',
+ url : '/v1/app_store/register',
+ data : JSON.stringify($scope.details)
+ }).then(successCallback, errorCallback)
+ }
+
+ $scope.add = function() {
+ $scope.details.components.push($scope.template);
+ }
+
+ $scope.remove = function(index) {
+ $scope.details.components.splice(index, 1);
+ }
+
+ function successCallback(response) {
+ $scope.message = "Application published successfully.";
+ setTimeout(function() {
+ $scope.$apply(function() {
+ window.location = '/#';
+ });
+ }, 5000);
+ }
+
+ function errorCallback(response) {
+ $scope.error = "Error in registering application configuration.";
+ }
+
+ } ]);
+
+controllers.controller("DeployAppController", [ '$scope', '$rootScope', '$http',
+ '$routeParams', function($scope, $rootScope, $http, $routeParams) {
+ $scope.id = $routeParams.id;
+
+ function successCallback(response) {
+ $scope.details = response.data;
+ $rootScope.$emit("hideLoadScreen", {});
+ }
+
+ function errorCallback(response) {
+ $rootScope.$emit("hideLoadScreen", {});
+ console.log("Error in downloading application template.");
+ }
+
+ $scope.launchApp = function(app) {
+ $rootScope.$emit("showLoadScreen", {});
+ $http({
+ method : 'POST',
+ url : '/v1/app_list/' + $scope.id,
+ data : JSON.stringify($scope.details.app)
+ }).then(function(data, status, headers, config) {
+ $rootScope.$emit("RefreshAppList", {});
+ window.location = '/#!/app/' + data.data.id;
+ }, function(data, status, headers, config) {
+ console.log('error', data, status);
+ });
+ }
+
+ $http({
+ method : 'GET',
+ url : '/v1/app_store/get/' + $scope.id
+ }).then(successCallback, errorCallback);
+
+}]);
+
+controllers.controller("LoadScreenController", [ '$scope', '$rootScope', '$http', function($scope, $rootScope, $http) {
+ $scope.loadScreen = "hide";
+
+ $rootScope.$on("showLoadScreen", function() {
+ $scope.loadScreen = "show";
+ });
+
+ $rootScope.$on("hideLoadScreen", function() {
+ $scope.loadScreen = "hide";
+ });
+
+}]);
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/filters.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/filters.js
new file mode 100644
index 0000000..bfb6838
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/filters.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.
+ */
+var app = angular.module("filters", []);
+
+app.filter("counterValue",function(){
+ return function(value){
+ var count=parseInt(value), suffix="";
+ if (count>=1000000){
+ count=Math.round(count/1000000);
+ suffix="M"
+ } else if (count>=1000){
+ count=Math.round(count/1000);
+ suffix="K"
+ }
+ return""+count+suffix
+ }
+});
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/routes.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/routes.js
new file mode 100644
index 0000000..d551913
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/routes.js
@@ -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.
+ */
\ No newline at end of file
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/services.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/services.js
new file mode 100644
index 0000000..d551913
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/javascript/services.js
@@ -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.
+ */
\ No newline at end of file
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/resources/appcatalog.properties b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/resources/appcatalog.properties
new file mode 100644
index 0000000..443b108
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/resources/appcatalog.properties
@@ -0,0 +1 @@
+solr_url=http://localhost:8983/solr/appcatalog/
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/resources/log4j.properties b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/resources/log4j.properties
new file mode 100644
index 0000000..9a0c608
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/resources/log4j.properties
@@ -0,0 +1,23 @@
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+log4j.rootLogger = INFO, CATALINA
+
+# Define all the appenders
+log4j.appender.CATALINA = org.apache.log4j.DailyRollingFileAppender
+log4j.appender.CATALINA.File = ${catalina.base}/logs/appcatalog.log
+log4j.appender.CATALINA.Append = true
+log4j.appender.CATALINA.Encoding = UTF-8
+# Roll-over the log once per day
+log4j.appender.CATALINA.DatePattern = '.'yyyy-MM-dd'.log'
+log4j.appender.CATALINA.layout = org.apache.log4j.PatternLayout
+log4j.appender.CATALINA.layout.ConversionPattern = %d [%t] %-5p %c- %m%n
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/.gitignore b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/.gitignore
new file mode 100644
index 0000000..a725465
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/.gitignore
@@ -0,0 +1 @@
+vendor/
\ No newline at end of file
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/WEB-INF/beans.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/WEB-INF/beans.xml
new file mode 100644
index 0000000..3326fa1
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/WEB-INF/beans.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
+ bean-discovery-mode="annotated">
+</beans>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/WEB-INF/web.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..fac7c5b
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <context-param>
+ <param-name>local-deployment</param-name>
+ <param-value>true</param-value>
+ </context-param>
+
+
+ <description>
+ YARN Application Catalog
+ </description>
+ <display-name>appcatalog</display-name>
+
+ <servlet>
+ <servlet-name>REST_API</servlet-name>
+ <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
+ <init-param>
+ <param-name>com.sun.jersey.config.property.packages</param-name>
+ <param-value>org.apache.hadoop.yarn.appcatalog.controller;com.wordnik.swagger.jaxrs.listing;com.wordnik.swagger.jaxrs.json</param-value>
+ </init-param>
+ <init-param>
+ <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
+ <param-value>true</param-value>
+ </init-param>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet>
+ <servlet-name>DefaultJaxrsConfig</servlet-name>
+ <servlet-class>com.wordnik.swagger.jaxrs.config.DefaultJaxrsConfig</servlet-class>
+ <init-param>
+ <param-name>api.version</param-name>
+ <param-value>3</param-value>
+ </init-param>
+ <init-param>
+ <param-name>swagger.api.basepath</param-name>
+ <param-value>http://localhost:8080/v1</param-value>
+ </init-param>
+ <load-on-startup>2</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>REST_API</servlet-name>
+ <url-pattern>/v1/*</url-pattern>
+ </servlet-mapping>
+
+ <filter>
+ <filter-name>CorsFilter</filter-name>
+ <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
+ </filter>
+ <filter-mapping>
+ <filter-name>CorsFilter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+
+</web-app>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/bootstrap-hadoop.css b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/bootstrap-hadoop.css
new file mode 100644
index 0000000..5aa6e46
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/bootstrap-hadoop.css
@@ -0,0 +1,1488 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@font-face {
+ font-family: 'Roboto';
+ font-weight: normal;
+ font-style: normal;
+ src: url('../vendor/roboto-fontface/fonts/roboto/Roboto-Regular.woff') format('woff');
+}
+.font-mixin {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+}
+.btn,
+.btn:focus {
+ outline: none;
+ font-family: 'Roboto', sans-serif;
+ text-transform: uppercase;
+ height: 34px;
+ font-size: 14px;
+ padding: 10px 20px;
+ line-height: 14px;
+}
+.btn .glyphicon,
+.btn:focus .glyphicon {
+ top: -1px;
+ float: left;
+}
+.box-shadow {
+ box-shadow: 0 0 2px 0 #1391c1;
+}
+.btn-disabled {
+ opacity: 0.6;
+ box-shadow: none;
+}
+.btn-default-disabled {
+ opacity: 0.6;
+ box-shadow: none;
+ color: #FFF;
+ background-color: #808793;
+ border: none;
+}
+.btn-default,
+.btn-default:focus {
+ color: #666;
+ background-color: #FFF;
+ border: 1px solid #CFD3D7;
+}
+.btn-default:hover,
+.btn-default:focus:hover {
+ color: #FFF;
+ background-color: #808793;
+}
+.btn-default:active,
+.btn-default:focus:active {
+ color: #666;
+ background-color: #FFF;
+ border: 1px solid #CFD3D7;
+ box-shadow: 0 0 2px 0 #1391c1;
+}
+.btn-default[disabled],
+.btn-default:focus[disabled],
+.btn-default.disabled,
+.btn-default:focus.disabled {
+ opacity: 0.6;
+ box-shadow: none;
+ color: #FFF;
+ background-color: #808793;
+ border: none;
+}
+.btn-default[disabled]:active,
+.btn-default:focus[disabled]:active,
+.btn-default.disabled:active,
+.btn-default:focus.disabled:active,
+.btn-default[disabled].active,
+.btn-default:focus[disabled].active,
+.btn-default.disabled.active,
+.btn-default:focus.disabled.active,
+.btn-default[disabled]:hover,
+.btn-default:focus[disabled]:hover,
+.btn-default.disabled:hover,
+.btn-default:focus.disabled:hover {
+ opacity: 0.6;
+ box-shadow: none;
+ color: #FFF;
+ background-color: #808793;
+ border: none;
+}
+.btn-primary-disabled {
+ opacity: 0.6;
+ box-shadow: none;
+ color: #D1E8D1;
+ background-color: #3FAE2A;
+ border: 1px solid #3FAE2A;
+}
+.btn-primary,
+.btn-primary:focus {
+ color: #FFF;
+ background-color: #3FAE2A;
+ border: 1px solid #3FAE2A;
+}
+.btn-primary:hover,
+.btn-primary:focus:hover {
+ color: #FFF;
+ background-color: #429929;
+ border: 1px solid #429929;
+}
+.btn-primary:active,
+.btn-primary:focus:active,
+.btn-primary.active,
+.btn-primary:focus.active {
+ color: #FFF;
+ background-color: #3FAE2A;
+ border: 1px solid #3FAE2A;
+ box-shadow: 0 0 2px 0 #1391c1;
+}
+.btn-primary[disabled],
+.btn-primary:focus[disabled],
+.btn-primary.disabled,
+.btn-primary:focus.disabled {
+ opacity: 0.6;
+ box-shadow: none;
+ color: #D1E8D1;
+ background-color: #3FAE2A;
+ border: 1px solid #3FAE2A;
+}
+.btn-primary[disabled]:active,
+.btn-primary:focus[disabled]:active,
+.btn-primary.disabled:active,
+.btn-primary:focus.disabled:active,
+.btn-primary[disabled].active,
+.btn-primary:focus[disabled].active,
+.btn-primary.disabled.active,
+.btn-primary:focus.disabled.active,
+.btn-primary[disabled]:hover,
+.btn-primary:focus[disabled]:hover,
+.btn-primary.disabled:hover,
+.btn-primary:focus.disabled:hover {
+ opacity: 0.6;
+ box-shadow: none;
+ color: #D1E8D1;
+ background-color: #3FAE2A;
+ border: 1px solid #3FAE2A;
+}
+.btn-secondary-disabled {
+ opacity: 0.6;
+ box-shadow: none;
+ color: #D1E8D1;
+ background-color: #429929;
+ border: 1px solid #3FAE2A;
+}
+.btn-secondary,
+.btn-secondary:focus {
+ color: #429929;
+ background-color: #FFF;
+ border: 1px solid #3FAE2A;
+}
+.btn-secondary:hover,
+.btn-secondary:focus:hover {
+ color: #FFF;
+ background-color: #429929;
+}
+.btn-secondary:active,
+.btn-secondary:focus:active {
+ color: #429929;
+ background-color: #FFF;
+ box-shadow: 0 0 2px 0 #1391c1;
+}
+.btn-secondary[disabled],
+.btn-secondary:focus[disabled],
+.btn-secondary.disabled,
+.btn-secondary:focus.disabled {
+ opacity: 0.6;
+ box-shadow: none;
+ color: #D1E8D1;
+ background-color: #429929;
+ border: 1px solid #3FAE2A;
+}
+.btn-secondary[disabled]:active,
+.btn-secondary:focus[disabled]:active,
+.btn-secondary.disabled:active,
+.btn-secondary:focus.disabled:active,
+.btn-secondary[disabled].active,
+.btn-secondary:focus[disabled].active,
+.btn-secondary.disabled.active,
+.btn-secondary:focus.disabled.active,
+.btn-secondary[disabled]:hover,
+.btn-secondary:focus[disabled]:hover,
+.btn-secondary.disabled:hover,
+.btn-secondary:focus.disabled:hover {
+ opacity: 0.6;
+ box-shadow: none;
+ color: #D1E8D1;
+ background-color: #429929;
+ border: 1px solid #3FAE2A;
+}
+.btn-success {
+ border: none;
+}
+.btn-regular-default-state {
+ background-color: #FFF;
+ color: #666;
+ border: 1px solid #cfd3d7;
+}
+.btn-primary-default-state {
+ background-color: #3FAE2A;
+ border: 1px solid #3FAE2A;
+ color: #FFF;
+}
+.btn-group.open .btn.dropdown-toggle,
+.dropdown.open .btn.dropdown-toggle {
+ box-shadow: inset 0px 0px 3px 0px #1391c1;
+}
+.btn-group.open .btn.dropdown-toggle,
+.dropdown.open .btn.dropdown-toggle,
+.btn-group.open .btn.dropdown-toggle.btn-default,
+.dropdown.open .btn.dropdown-toggle.btn-default {
+ background-color: #FFF;
+ color: #666;
+ border: 1px solid #cfd3d7;
+}
+.btn-group.open .btn.dropdown-toggle:hover,
+.dropdown.open .btn.dropdown-toggle:hover,
+.btn-group.open .btn.dropdown-toggle.btn-default:hover,
+.dropdown.open .btn.dropdown-toggle.btn-default:hover {
+ background-color: #FFF;
+ color: #666;
+ border: 1px solid #cfd3d7;
+}
+.btn-group.open .btn.dropdown-toggle + .dropdown-menu > li > a:hover,
+.dropdown.open .btn.dropdown-toggle + .dropdown-menu > li > a:hover,
+.btn-group.open .btn.dropdown-toggle.btn-default + .dropdown-menu > li > a:hover,
+.dropdown.open .btn.dropdown-toggle.btn-default + .dropdown-menu > li > a:hover {
+ background-color: #808793;
+ color: #FFF;
+}
+.btn-group.open .btn.dropdown-toggle.btn-primary,
+.dropdown.open .btn.dropdown-toggle.btn-primary {
+ background-color: #3FAE2A;
+ border: 1px solid #3FAE2A;
+ color: #FFF;
+}
+.btn-group.open .btn.dropdown-toggle.btn-primary:hover,
+.dropdown.open .btn.dropdown-toggle.btn-primary:hover {
+ background-color: #3FAE2A;
+ border: 1px solid #3FAE2A;
+ color: #FFF;
+}
+.btn-group.open .btn.dropdown-toggle.btn-primary + .dropdown-menu > li > a:hover,
+.dropdown.open .btn.dropdown-toggle.btn-primary + .dropdown-menu > li > a:hover {
+ background-color: #429929;
+ color: #FFF;
+}
+.btn-group.open .dropdown-menu,
+.dropdown.open .dropdown-menu {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ border-radius: 2px;
+ font-size: 14px;
+ min-width: 200px;
+ background: #FFF;
+ color: #666;
+ border: 1px solid #cfd3d7;
+}
+.btn-group.open .dropdown-menu > li,
+.dropdown.open .dropdown-menu > li {
+ margin-bottom: 1px;
+}
+.btn-group.open .dropdown-menu > li > a,
+.dropdown.open .dropdown-menu > li > a {
+ height: 24px;
+}
+.btn-group .btn.dropdown-toggle:first-child,
+.dropdown .btn.dropdown-toggle:first-child {
+ min-width: 80px;
+}
+.btn-group .btn.dropdown-toggle.disabled,
+.dropdown .btn.dropdown-toggle.disabled,
+.btn-group .btn.dropdown-toggle[disabled],
+.dropdown .btn.dropdown-toggle[disabled] {
+ opacity: 0.6;
+}
+input.form-control {
+ font-size: 14px;
+ border-radius: 2px;
+ color: #666;
+ border: 1px solid #CFD3D7;
+ height: 34px;
+ padding: 10px;
+}
+input.form-control:focus {
+ border-color: #1291c1;
+ box-shadow: none;
+}
+.help-block {
+ color: #999999;
+ font-size: 14px;
+}
+.help-block.validation-block {
+ color: #999999;
+ margin-top: 10px;
+}
+.help-block.validation-block::before {
+ position: relative;
+ top: 2px;
+ margin-right: 5px;
+ font-family: 'Glyphicons Halflings';
+}
+.has-success input.form-control {
+ color: #666;
+ border: 1px solid #1EB475;
+}
+.has-success input.form-control:focus {
+ border-color: #1EB475;
+ box-shadow: none;
+}
+.has-success .help-block.validation-block::before {
+ content: '\e084';
+ color: #1EB475;
+}
+.has-error input.form-control {
+ color: #666;
+ border: 1px solid #EF6162;
+}
+.has-error input.form-control:focus {
+ border-color: #EF6162;
+ box-shadow: none;
+}
+.has-error .help-block.validation-block::before {
+ content: '\e083';
+ color: #EF6162;
+}
+.has-warning input.form-control {
+ color: #666;
+ border: 1px solid #E98A40;
+}
+.has-warning input.form-control:focus {
+ border-color: #E98A40;
+ box-shadow: none;
+}
+.has-warning .help-block.validation-block::before {
+ content: '\e101';
+ color: #E98A40;
+}
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+ color: #999999;
+ border-color: #cccccc;
+ background-color: #dddddd;
+}
+h2.table-title {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ margin-top: 10px;
+ font-size: 20px;
+}
+.table {
+ color: #666;
+ font-size: 13px;
+}
+.table thead,
+.table tfoot {
+ color: #999;
+}
+.table input[type="checkbox"] + label {
+ position: relative;
+ line-height: 1.3em;
+ font-size: initial;
+ top: 4px;
+ margin-bottom: 0;
+}
+.table thead > tr > th {
+ border-bottom-color: #EEE;
+}
+.table tfoot > tr:first-of-type > td {
+ border-top-width: 2px;
+ border-top-color: #EEE;
+}
+.table > tbody > tr > td {
+ border-top-color: #EEE;
+}
+.table > tbody > tr.active {
+ background-color: #EEE;
+}
+.table > tbody > tr.active > td {
+ background-color: #EEE;
+}
+.table.table-hover .action {
+ visibility: hidden;
+ padding: 0;
+ line-height: 1;
+}
+.table.table-hover .action:hover {
+ text-decoration: none;
+}
+.table.table-hover > tbody > tr {
+ border-width: 0 1px 1px;
+ border-style: solid;
+ border-color: #EEE transparent;
+}
+.table.table-hover > tbody > tr > td {
+ border-width: 0;
+}
+.table.table-hover > tbody > tr:hover {
+ border-color: #A7DFF2;
+ background-color: #E7F6FC;
+}
+.table.table-hover > tbody > tr:hover > td {
+ border-top: 1px solid #A7DFF2;
+ background-color: #E7F6FC;
+}
+.table.table-hover > tbody > tr:hover > td .action {
+ visibility: visible;
+}
+.table.table-hover > tbody > tr:first-of-type > td {
+ border-top: 1px solid transparent;
+}
+.table.table-hover > tbody > tr:first-of-type:hover > td {
+ border-color: #A7DFF2;
+}
+.pagination-block .pagination-block-item {
+ float: left;
+ padding: 0 5px;
+}
+.pagination-block .pagination-block-item a,
+.pagination-block .pagination-block-item a:visited,
+.pagination-block .pagination-block-item a:focus {
+ text-decoration: none;
+}
+.pagination-block .pagination-block-item select {
+ border: none;
+ background-color: transparent;
+ color: #1491C1;
+}
+.nav.nav-tabs {
+ border: none;
+ margin-bottom: 20px;
+}
+.nav.nav-tabs li a {
+ border-width: 0;
+ border-radius: 0;
+ border-bottom: 2px solid transparent;
+ color: #666;
+ text-transform: uppercase;
+}
+.nav.nav-tabs li a:hover,
+.nav.nav-tabs li a:active,
+.nav.nav-tabs li a:focus {
+ color: #333;
+ border-top-width: 0;
+ border-left-width: 0;
+ border-right-width: 0;
+ background: none;
+}
+.nav.nav-tabs li a .badge.badge-important {
+ display: inline;
+}
+.nav.nav-tabs li.active a {
+ color: #333;
+ border-bottom: 2px solid #3FAE2A;
+}
+.nav-tabs-left li,
+.nav-tabs-right li {
+ float: none;
+ margin-bottom: 2px;
+}
+.nav-tabs-left li a,
+.nav-tabs-right li a {
+ margin-right: 0;
+}
+.nav-tabs-left li {
+ margin-right: -1px;
+}
+.nav-tabs-left li a {
+ border: 2px solid transparent !important;
+}
+.nav-tabs-left li.active a,
+.nav-tabs-left li.active a:hover,
+.nav-tabs-left li.active a:active,
+.nav-tabs-left li.active a:focus {
+ border-right: 2px solid #3FAE2A !important;
+}
+.nav-tabs-right li {
+ margin-left: -1px;
+}
+.nav-tabs-right li a {
+ border: 2px solid transparent !important;
+}
+.nav-tabs-right li.active a,
+.nav-tabs-right li.active a:hover,
+.nav-tabs-right li.active a:active,
+.nav-tabs-right li.active a:focus {
+ border-left: 2px solid #3FAE2A !important;
+}
+.wizard {
+ border: 2px solid #ebecf1;
+}
+.wizard .wizard-header h3 {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ font-size: 20px;
+ color: #333;
+ margin: 15px 20px;
+}
+.wizard .wizard-body {
+ overflow: hidden;
+ margin: 0;
+}
+.wizard .wizard-body .wizard-content {
+ background: #ebecf1;
+ padding-top: 25px;
+ float: left;
+ margin-bottom: -99999px;
+ padding-bottom: 99999px;
+}
+.wizard .wizard-body .wizard-content .step-title {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ font-weight: bold;
+ font-size: 18px;
+ color: #666;
+}
+.wizard .wizard-body .wizard-content .step-description {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ font-size: 14px;
+ line-height: 1.4;
+ color: #999;
+}
+.wizard .wizard-body .wizard-content .panel.panel-default {
+ border: none;
+ box-shadow: none;
+ margin-top: 20px;
+}
+.wizard .wizard-body .wizard-content .panel.panel-default .panel-body {
+ padding: 30px 20px;
+}
+.wizard .wizard-body .wizard-nav {
+ min-height: 550px;
+ padding-top: 25px;
+ background-color: #323544;
+ float: left;
+ margin-bottom: -99999px;
+ padding-bottom: 99999px;
+}
+.wizard .wizard-body .wizard-nav .nav li {
+ padding: 0px 15px;
+}
+.wizard .wizard-body .wizard-nav .nav li a {
+ height: 48px;
+ padding: 0px 5px;
+ display: table-cell;
+ vertical-align: middle;
+}
+.wizard .wizard-body .wizard-nav .nav li .step-marker {
+ position: absolute;
+ top: 9px;
+ line-height: 16px;
+ text-align: center;
+ width: 20px;
+ height: 20px;
+ border: 2px solid #1EB475;
+ border-radius: 50%;
+ font-size: 12px;
+ font-style: inherit;
+ color: #1EB475;
+ background-color: #323544;
+}
+.wizard .wizard-body .wizard-nav .nav li .step-name {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ font-size: 14px;
+ color: #999;
+ margin-left: 30px;
+}
+.wizard .wizard-body .wizard-nav .nav li .step-index {
+ line-height: 18px;
+}
+.wizard .wizard-body .wizard-nav .nav li .step-description {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ font-size: 12px;
+ color: #999;
+ margin-left: 30px;
+}
+.wizard .wizard-body .wizard-nav .nav li.completed .step-marker {
+ background-color: #1EB475;
+ color: white;
+ font-size: 10px;
+ padding-left: 2px;
+}
+.wizard .wizard-body .wizard-nav .nav li.completed .step-marker .step-index {
+ display: none;
+}
+.wizard .wizard-body .wizard-nav .nav li.completed .step-marker:after {
+ font-family: "Glyphicons Halflings";
+ content: "\e013";
+ position: relative;
+ top: 1px;
+ left: -1px;
+}
+.wizard .wizard-body .wizard-nav .nav li.completed:after {
+ width: 2px;
+ height: 100%;
+ position: absolute;
+ background-color: #1EB475;
+ content: "";
+ top: 25px;
+ left: 29px;
+}
+.wizard .wizard-body .wizard-nav .nav li.completed:last-child:after {
+ content: none;
+}
+.wizard .wizard-body .wizard-nav .nav li.active .step-name {
+ font-weight: bold;
+}
+.wizard .wizard-body .wizard-nav .nav li.disabled .step-marker {
+ color: #666;
+ border-color: #666;
+}
+.wizard .wizard-body .wizard-nav .nav li.disabled .step-name,
+.wizard .wizard-body .wizard-nav .nav li.disabled .step-description {
+ color: #666;
+}
+.wizard .wizard-body .wizard-nav .nav li.disabled.completed .step-marker {
+ background-color: #1EB475;
+ border: 2px solid #1EB475;
+ color: white;
+}
+.wizard .wizard-body .wizard-nav .nav-pills > li.active > a,
+.wizard .wizard-body .wizard-nav .nav-pills > li.active > a:focus,
+.wizard .wizard-body .wizard-nav .nav-pills > li.active > a:hover,
+.wizard .wizard-body .wizard-nav .nav > li > a:focus,
+.wizard .wizard-body .wizard-nav .nav > li > a:hover {
+ background-color: inherit;
+}
+.wizard .wizard-body .wizard-footer {
+ background: white;
+ padding: 15px 20px;
+}
+.wizard .wizard-body .wizard-footer button {
+ margin: 0 10px;
+}
+.checkbox-disabled-style {
+ background-color: #b2b8c1;
+ border-color: #b2b8c1;
+}
+input[type="checkbox"]:not(:checked),
+input[type="radio"]:not(:checked),
+input[type="checkbox"]:checked,
+input[type="radio"]:checked {
+ display: none;
+}
+input[type="checkbox"]:not(:checked) + label,
+input[type="radio"]:not(:checked) + label,
+input[type="checkbox"]:checked + label,
+input[type="radio"]:checked + label {
+ position: relative;
+ padding-left: 20px;
+}
+input[type="checkbox"]:not(:checked) + label:hover:before,
+input[type="radio"]:not(:checked) + label:hover:before,
+input[type="checkbox"]:checked + label:hover:before,
+input[type="radio"]:checked + label:hover:before {
+ border-color: #1491C1;
+ background-color: #1491C1;
+}
+input[type="checkbox"]:checked + label:before,
+input[type="radio"]:checked + label:before {
+ background-color: #1491C1;
+ border-color: #1491C1;
+}
+input[type="checkbox"][disabled] + label:before,
+input[type="radio"][disabled] + label:before,
+input[type="checkbox"].disabled + label:before,
+input[type="radio"].disabled + label:before,
+input[type="checkbox"][disabled] + label:hover:before,
+input[type="radio"][disabled] + label:hover:before,
+input[type="checkbox"].disabled + label:hover:before,
+input[type="radio"].disabled + label:hover:before {
+ background-color: #b2b8c1;
+ border-color: #b2b8c1;
+}
+input[type="checkbox"] + label:before {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 4px;
+ width: 10px;
+ height: 10px;
+ box-sizing: border-box;
+ border-radius: 2px;
+ border-width: 1px;
+ border-style: solid;
+ border-color: #ddd;
+}
+input[type="checkbox"]:checked + label:after {
+ content: '\2714';
+ color: #FFF;
+ position: absolute;
+ top: 0;
+ left: 2px;
+ font-size: 9px;
+}
+input[type="radio"] + label:before,
+input.radio + label:before {
+ content: '';
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 12px;
+ height: 12px;
+ box-sizing: border-box;
+ border-radius: 12px;
+ border-width: 1px;
+ border-style: solid;
+ border-color: #ddd;
+}
+input[type="radio"]:checked + label:after,
+input.radio:checked + label:after {
+ content: '';
+ background-color: #FFF;
+ position: absolute;
+ top: 3px;
+ left: 3px;
+ width: 6px;
+ height: 6px;
+ border-radius: 6px;
+}
+.navigation-bar-container {
+ height: auto;
+ width: 230px;
+ background-color: #323544;
+ padding: 0;
+ -ms-overflow-style: none;
+ transition: width 0.5s ease-out;
+ -webkit-font-smoothing: antialiased;
+}
+.navigation-bar-container ul.nav.side-nav-header {
+ width: 230px;
+ transition: width 0.5s ease-out;
+}
+.navigation-bar-container ul.nav.side-nav-header li.navigation-header {
+ background: #313d54;
+ padding: 15px 5px 15px 25px;
+ height: 55px;
+}
+.navigation-bar-container ul.nav.side-nav-header li.navigation-header > a.ambari-logo {
+ padding: 0;
+}
+.navigation-bar-container ul.nav.side-nav-header li.navigation-header > a.ambari-logo > img {
+ height: 25px;
+ float: left;
+ margin-left: -3px;
+}
+.navigation-bar-container ul.nav.side-nav-header li.navigation-header .btn-group {
+ cursor: pointer;
+ margin-top: 3px;
+}
+.navigation-bar-container ul.nav.side-nav-header li.navigation-header .btn-group:hover span.ambari-header,
+.navigation-bar-container ul.nav.side-nav-header li.navigation-header .btn-group:hover span.toggle-icon {
+ color: #fff;
+}
+.navigation-bar-container ul.nav.side-nav-header li.navigation-header .btn-group span.ambari-header {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ font-size: 20px;
+ width: 55px;
+ display: inline;
+ color: #b8bec4;
+ padding: 0 8px 0 10px;
+}
+.navigation-bar-container ul.nav.side-nav-header li.navigation-header .btn-group span.toggle-icon {
+ margin-bottom: 5px;
+ font-size: 13px;
+ display: inline-block;
+ vertical-align: middle;
+ color: #b8bec4;
+}
+.navigation-bar-container ul.nav.side-nav-header li.navigation-header .btn-group.open .dropdown-toggle {
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+.navigation-bar-container ul.nav.side-nav-header li.navigation-header ul.dropdown-menu {
+ top: 30px;
+}
+.navigation-bar-container ul.nav.side-nav-header li.navigation-header ul.dropdown-menu li > a {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ font-size: 14px;
+ color: #666;
+ line-height: 1.42;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.navigation-bar-container ul.nav.side-nav-header li.navigation-header ul.dropdown-menu li > a:hover {
+ background: #f5f5f5;
+}
+.navigation-bar-container ul.nav.side-nav-menu,
+.navigation-bar-container ul.nav.side-nav-footer {
+ background-color: #323544;
+ width: 230px;
+ transition: width 0.5s ease-out;
+}
+.navigation-bar-container ul.nav.side-nav-menu li,
+.navigation-bar-container ul.nav.side-nav-footer li {
+ padding: 0;
+ margin: 0;
+}
+.navigation-bar-container ul.nav.side-nav-menu li.navigation-footer > a,
+.navigation-bar-container ul.nav.side-nav-footer li.navigation-footer > a,
+.navigation-bar-container ul.nav.side-nav-menu li.submenu-li > a,
+.navigation-bar-container ul.nav.side-nav-footer li.submenu-li > a,
+.navigation-bar-container ul.nav.side-nav-menu li.mainmenu-li > a,
+.navigation-bar-container ul.nav.side-nav-footer li.mainmenu-li > a {
+ display: table-cell;
+ vertical-align: middle;
+ width: 230px;
+ border-radius: 0;
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ white-space: nowrap;
+}
+.navigation-bar-container ul.nav.side-nav-menu li.navigation-footer > a .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-footer li.navigation-footer > a .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-menu li.submenu-li > a .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-footer li.submenu-li > a .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-menu li.mainmenu-li > a .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-footer li.mainmenu-li > a .navigation-menu-item {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ font-size: 14px;
+ color: #b8bec4;
+ padding-left: 8px;
+}
+.navigation-bar-container ul.nav.side-nav-menu li.navigation-footer > a .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-footer li.navigation-footer > a .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-menu li.submenu-li > a .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-footer li.submenu-li > a .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-menu li.mainmenu-li > a .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-footer li.mainmenu-li > a .navigation-icon {
+ line-height: 18px;
+ font-size: 18px;
+ color: #b8bec4;
+}
+.navigation-bar-container ul.nav.side-nav-menu li.navigation-footer > a .toggle-icon,
+.navigation-bar-container ul.nav.side-nav-footer li.navigation-footer > a .toggle-icon,
+.navigation-bar-container ul.nav.side-nav-menu li.submenu-li > a .toggle-icon,
+.navigation-bar-container ul.nav.side-nav-footer li.submenu-li > a .toggle-icon,
+.navigation-bar-container ul.nav.side-nav-menu li.mainmenu-li > a .toggle-icon,
+.navigation-bar-container ul.nav.side-nav-footer li.mainmenu-li > a .toggle-icon {
+ line-height: 14px;
+ font-size: 14px;
+ color: #b8bec4;
+ padding: 3px 5px 3px 10px;
+}
+.navigation-bar-container ul.nav.side-nav-menu li.navigation-footer > a,
+.navigation-bar-container ul.nav.side-nav-footer li.navigation-footer > a,
+.navigation-bar-container ul.nav.side-nav-menu li.mainmenu-li > a,
+.navigation-bar-container ul.nav.side-nav-footer li.mainmenu-li > a {
+ padding: 10px 5px 10px 20px;
+}
+.navigation-bar-container ul.nav.side-nav-menu li.submenu-li > a,
+.navigation-bar-container ul.nav.side-nav-footer li.submenu-li > a {
+ padding: 10px 5px 10px 25px;
+}
+.navigation-bar-container ul.nav.side-nav-menu li.navigation-footer,
+.navigation-bar-container ul.nav.side-nav-footer li.navigation-footer {
+ background: #313d54;
+ height: 50px;
+}
+.navigation-bar-container ul.nav.side-nav-menu li.navigation-footer a .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-footer li.navigation-footer a .navigation-icon {
+ color: #31823a;
+ font-size: 20px;
+ position: relative;
+ padding: 0 15px;
+ left: calc(30%);
+}
+.navigation-bar-container ul.nav.side-nav-menu li.navigation-footer a .navigation-icon:hover,
+.navigation-bar-container ul.nav.side-nav-footer li.navigation-footer a .navigation-icon:hover {
+ color: #fff;
+}
+.navigation-bar-container ul.nav.side-nav-menu li > ul > li,
+.navigation-bar-container ul.nav.side-nav-footer li > ul > li {
+ background-color: #323544;
+}
+.navigation-bar-container ul.nav.side-nav-menu li > ul > li a,
+.navigation-bar-container ul.nav.side-nav-footer li > ul > li a {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ font-size: 14px;
+ color: #999;
+}
+.navigation-bar-container ul.nav.side-nav-menu li > ul > li a .submenu-icon,
+.navigation-bar-container ul.nav.side-nav-footer li > ul > li a .submenu-icon {
+ line-height: 14px;
+ font-size: 14px;
+}
+.navigation-bar-container ul.nav.side-nav-menu li > ul > li > a:hover,
+.navigation-bar-container ul.nav.side-nav-footer li > ul > li > a:hover,
+.navigation-bar-container ul.nav.side-nav-menu li > a:hover,
+.navigation-bar-container ul.nav.side-nav-footer li > a:hover {
+ background: #404351;
+ cursor: pointer;
+ color: #fff;
+}
+.navigation-bar-container ul.nav.side-nav-menu li > ul > li > a:hover .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-footer li > ul > li > a:hover .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-menu li > a:hover .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-footer li > a:hover .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-menu li > ul > li > a:hover .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-footer li > ul > li > a:hover .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-menu li > a:hover .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-footer li > a:hover .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-menu li > ul > li > a:hover .toggle-icon,
+.navigation-bar-container ul.nav.side-nav-footer li > ul > li > a:hover .toggle-icon,
+.navigation-bar-container ul.nav.side-nav-menu li > a:hover .toggle-icon,
+.navigation-bar-container ul.nav.side-nav-footer li > a:hover .toggle-icon,
+.navigation-bar-container ul.nav.side-nav-menu li > ul > li > a:hover .submenu-item,
+.navigation-bar-container ul.nav.side-nav-footer li > ul > li > a:hover .submenu-item,
+.navigation-bar-container ul.nav.side-nav-menu li > a:hover .submenu-item,
+.navigation-bar-container ul.nav.side-nav-footer li > a:hover .submenu-item {
+ color: #fff;
+}
+.navigation-bar-container ul.nav.side-nav-menu li.active:not(.has-sub-menu),
+.navigation-bar-container ul.nav.side-nav-footer li.active:not(.has-sub-menu),
+.navigation-bar-container ul.nav.side-nav-menu li.active.collapsed,
+.navigation-bar-container ul.nav.side-nav-footer li.active.collapsed {
+ background: #404351;
+ cursor: pointer;
+}
+.navigation-bar-container ul.nav.side-nav-menu li.active:not(.has-sub-menu) > a,
+.navigation-bar-container ul.nav.side-nav-footer li.active:not(.has-sub-menu) > a,
+.navigation-bar-container ul.nav.side-nav-menu li.active.collapsed > a,
+.navigation-bar-container ul.nav.side-nav-footer li.active.collapsed > a {
+ color: #fff;
+}
+.navigation-bar-container ul.nav.side-nav-menu li.active:not(.has-sub-menu) > a .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-footer li.active:not(.has-sub-menu) > a .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-menu li.active.collapsed > a .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-footer li.active.collapsed > a .navigation-menu-item,
+.navigation-bar-container ul.nav.side-nav-menu li.active:not(.has-sub-menu) > a .submenu-item,
+.navigation-bar-container ul.nav.side-nav-footer li.active:not(.has-sub-menu) > a .submenu-item,
+.navigation-bar-container ul.nav.side-nav-menu li.active.collapsed > a .submenu-item,
+.navigation-bar-container ul.nav.side-nav-footer li.active.collapsed > a .submenu-item,
+.navigation-bar-container ul.nav.side-nav-menu li.active:not(.has-sub-menu) > a .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-footer li.active:not(.has-sub-menu) > a .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-menu li.active.collapsed > a .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-footer li.active.collapsed > a .navigation-icon,
+.navigation-bar-container ul.nav.side-nav-menu li.active:not(.has-sub-menu) > a .toggle-icon,
+.navigation-bar-container ul.nav.side-nav-footer li.active:not(.has-sub-menu) > a .toggle-icon,
+.navigation-bar-container ul.nav.side-nav-menu li.active.collapsed > a .toggle-icon,
+.navigation-bar-container ul.nav.side-nav-footer li.active.collapsed > a .toggle-icon {
+ color: #fff;
+}
+.navigation-bar-container ul.nav.side-nav-menu li.active:not(.has-sub-menu) > a:after,
+.navigation-bar-container ul.nav.side-nav-footer li.active:not(.has-sub-menu) > a:after,
+.navigation-bar-container ul.nav.side-nav-menu li.active.collapsed > a:after,
+.navigation-bar-container ul.nav.side-nav-footer li.active.collapsed > a:after {
+ left: 0;
+ top: 50%;
+ border: solid transparent;
+ border-width: 10px 7px;
+ content: " ";
+ height: 0;
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+ border-color: transparent;
+ border-left-color: #31823a;
+ margin-top: -12px;
+}
+.navigation-bar-container ul.nav.side-nav-menu .more-actions,
+.navigation-bar-container ul.nav.side-nav-footer .more-actions {
+ display: none;
+ position: absolute;
+ top: 14px;
+ right: 33px;
+ line-height: 25px;
+ width: 20px;
+ text-align: center;
+ font-size: 14px;
+ cursor: pointer;
+ vertical-align: middle;
+ color: #fff;
+}
+.navigation-bar-container ul.nav.side-nav-menu .more-actions .dropdown-menu > li > a,
+.navigation-bar-container ul.nav.side-nav-footer .more-actions .dropdown-menu > li > a {
+ color: #666;
+}
+.navigation-bar-container ul.nav.side-nav-menu .more-actions .dropdown-menu > li > a i,
+.navigation-bar-container ul.nav.side-nav-footer .more-actions .dropdown-menu > li > a i {
+ color: #666;
+}
+.navigation-bar-container ul.nav.side-nav-menu .more-actions .dropdown-menu > li > a:hover,
+.navigation-bar-container ul.nav.side-nav-footer .more-actions .dropdown-menu > li > a:hover {
+ background: #f5f5f5;
+}
+.navigation-bar-container ul.nav.side-nav-menu .menu-item-name,
+.navigation-bar-container ul.nav.side-nav-footer .menu-item-name {
+ display: inline-block;
+ vertical-align: bottom;
+ max-width: 100px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -o-text-overflow: ellipsis;
+ -ms-text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.navigation-bar-container .nav-pills > li.active > a,
+.navigation-bar-container .nav-pills > li.active > a:focus,
+.navigation-bar-container .nav-pills > li.active > a:hover,
+.navigation-bar-container .nav > li > a:focus,
+.navigation-bar-container .nav > li > a:hover {
+ background-color: inherit;
+}
+.navigation-bar-container.collapsed {
+ width: 50px;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-header {
+ width: 50px;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-header li.navigation-header {
+ padding: 15px 0 15px 15px;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-header li.navigation-header span.ambari-header,
+.navigation-bar-container.collapsed ul.nav.side-nav-header li.navigation-header span.toggle-icon,
+.navigation-bar-container.collapsed ul.nav.side-nav-header li.navigation-header .dropdown-menu {
+ display: none;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-menu,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer {
+ width: 50px;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li a,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li a {
+ padding: 15px 0 15px 15px;
+ width: 50px;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li a .navigation-menu-item,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li a .navigation-menu-item,
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li a .toggle-icon,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li a .toggle-icon {
+ display: none;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li.navigation-footer a .navigation-icon,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li.navigation-footer a .navigation-icon {
+ padding: 0 5px;
+ left: 0;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li ul.sub-menu,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li ul.sub-menu {
+ display: none;
+ width: 230px;
+ position: absolute;
+ z-index: 100;
+ top: 0;
+ left: 50px;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li.submenu-li > a,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li.submenu-li > a {
+ padding: 10px 5px 10px 25px;
+ width: 230px;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li.active,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li.active {
+ background: #404351;
+ cursor: pointer;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li.active > a,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li.active > a {
+ color: #fff;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li.active > a .navigation-menu-item,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li.active > a .navigation-menu-item,
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li.active > a .submenu-item,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li.active > a .submenu-item,
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li.active > a .navigation-icon,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li.active > a .navigation-icon,
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li.active > a .toggle-icon,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li.active > a .toggle-icon {
+ color: #fff;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-menu li.active > a:after,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer li.active > a:after {
+ left: 0;
+ top: 50%;
+ border: solid transparent;
+ border-width: 12px 6px;
+ content: " ";
+ height: 0;
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+ border-color: transparent;
+ border-left-color: #31823a;
+ margin-top: -12px;
+}
+.navigation-bar-container.collapsed ul.nav.side-nav-menu .more-actions,
+.navigation-bar-container.collapsed ul.nav.side-nav-footer .more-actions {
+ display: none;
+}
+.navigation-bar-fit-height {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 2079;
+}
+.navigation-bar-fit-height .side-nav-header {
+ position: absolute;
+ top: 0;
+}
+.navigation-bar-fit-height .side-nav-menu {
+ position: absolute;
+ top: 55px;
+ bottom: 50px;
+}
+.navigation-bar-fit-height .side-nav-footer {
+ position: absolute;
+ bottom: 0;
+}
+.navigation-bar-fit-height .more-actions .dropdown-menu {
+ position: fixed;
+ top: auto;
+ left: auto;
+}
+.navigation-bar-fit-height .navigation-bar-container {
+ height: 100%;
+}
+.navigation-bar-fit-height .navigation-bar-container .side-nav-menu {
+ overflow-y: auto;
+}
+.notifications-group {
+ position: relative;
+ top: 1px;
+}
+#notifications-dropdown.dropdown-menu {
+ min-width: 300px;
+ max-width: 300px;
+ min-height: 150px;
+ padding: 0px;
+ z-index: 1000;
+ right: -50px;
+ left: auto;
+ top: 260%;
+ border: none;
+ -webkit-box-shadow: 0px 2px 10px 2px rgba(0, 0, 0, 0.29);
+ -moz-box-shadow: 0px 2px 10px 2px rgba(0, 0, 0, 0.29);
+ box-shadow: 0px 2px 10px 2px rgba(0, 0, 0, 0.29);
+}
+#notifications-dropdown.dropdown-menu .popup-arrow-up {
+ position: absolute;
+ right: 37px;
+ top: -40px;
+ width: 40px;
+ height: 40px;
+ overflow: hidden;
+}
+#notifications-dropdown.dropdown-menu .popup-arrow-up:after {
+ content: "";
+ position: absolute;
+ width: 20px;
+ height: 20px;
+ background: #fff;
+ transform: rotate(45deg);
+ top: 30px;
+ left: 10px;
+ box-shadow: -1px -1px 10px -2px rgba(0, 0, 0, 0.5);
+}
+#notifications-dropdown.dropdown-menu .notifications-header {
+ border-bottom: 1px solid #eee;
+ padding: 15px 20px;
+}
+#notifications-dropdown.dropdown-menu .notifications-header .notifications-title {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ font-size: 16px;
+}
+#notifications-dropdown.dropdown-menu .notifications-body {
+ padding: 0px 15px;
+ overflow: auto;
+ max-height: 500px;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .no-alert-text {
+ padding: 15px 5px;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table-controls {
+ padding: 10px 0px;
+ margin: 0px;
+ border-bottom: 1px solid #eee;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table-controls .state-filter {
+ padding: 0px;
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ font-size: 12px;
+ color: #666;
+ position: relative;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table-controls .state-filter .form-control.filter-select {
+ font-size: 12px;
+ color: #666;
+ height: 25px;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table.alerts-table {
+ margin-top: 0px;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table.alerts-table tbody tr {
+ cursor: pointer;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table.alerts-table tbody tr.no-alert-tr:hover {
+ cursor: default;
+ border-color: transparent;
+ border-bottom-color: #eee;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table.alerts-table tbody tr.no-alert-tr:hover > td {
+ border-color: transparent;
+ background-color: white;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table.alerts-table tbody td.status {
+ width: 9%;
+ padding: 15px 3px;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table.alerts-table tbody td.status .alert-state-CRITICAL {
+ color: #EF6162;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table.alerts-table tbody td.status .alert-state-WARNING {
+ color: #E98A40;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table.alerts-table tbody td.content {
+ width: 90%;
+ padding: 15px 3px 10px 3px;
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ line-height: 1.3;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table.alerts-table tbody td.content .name {
+ font-weight: bold;
+ font-size: 14px;
+ color: #333;
+ margin-bottom: 5px;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table.alerts-table tbody td.content .description {
+ font-size: 12px;
+ color: #666;
+ margin-bottom: 4px;
+ display: block;
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ max-height: 47px;
+ /* For firefox */
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ /* Break long urls*/
+ overflow-wrap: break-word;
+ word-wrap: break-word;
+ -ms-word-break: break-all;
+ word-break: break-all;
+ word-break: break-word;
+ /* Adds a hyphen where the word breaks*/
+ -ms-hyphens: auto;
+ -moz-hyphens: auto;
+ -webkit-hyphens: auto;
+ hyphens: auto;
+}
+#notifications-dropdown.dropdown-menu .notifications-body .table.alerts-table tbody td.content .timestamp {
+ text-align: right;
+ font-size: 11px;
+ color: #999;
+}
+#notifications-dropdown.dropdown-menu .notifications-footer {
+ border-top: 1px solid #eee;
+ padding: 15px;
+}
+.modal-backdrop {
+ background-color: #808080;
+}
+.modal .modal-content {
+ border-radius: 2px;
+}
+.modal .modal-content .modal-header,
+.modal .modal-content .modal-body,
+.modal .modal-content .modal-footer {
+ padding-left: 20px;
+ padding-right: 20px;
+}
+.modal .modal-content .modal-header {
+ border-bottom: none;
+ padding-top: 20px;
+ color: #666;
+ font-size: 20px;
+}
+.modal .modal-content .modal-header h4 {
+ margin: 0;
+ color: inherit;
+ font-size: inherit;
+}
+.modal .modal-content .modal-body {
+ color: #666;
+ font-size: 12px;
+}
+.modal .modal-content .modal-footer {
+ border-top: none;
+ padding-bottom: 20px;
+}
+.modal .modal-content .modal-footer .btn ~ .btn {
+ margin-left: 10px;
+}
+.accordion .panel-group,
+.wizard .wizard-body .wizard-content .accordion .panel-group {
+ margin-bottom: 0px;
+}
+.accordion .panel-group .panel,
+.wizard .wizard-body .wizard-content .accordion .panel-group .panel {
+ border-radius: 0px;
+ border: none;
+ margin-top: 0px;
+}
+.accordion .panel-group .panel .panel-heading,
+.wizard .wizard-body .wizard-content .accordion .panel-group .panel .panel-heading {
+ height: 50px;
+ padding: 15px 10px;
+ border: 1px solid;
+ border-color: #ddd transparent;
+ border-top: none;
+ background: #fff;
+}
+.accordion .panel-group .panel .panel-heading .panel-title,
+.wizard .wizard-body .wizard-content .accordion .panel-group .panel .panel-heading .panel-title {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+}
+.accordion .panel-group .panel .panel-heading .panel-title > a,
+.wizard .wizard-body .wizard-content .accordion .panel-group .panel .panel-heading .panel-title > a {
+ font-size: 18px;
+ color: #333;
+}
+.accordion .panel-group .panel .panel-heading .panel-title > i,
+.wizard .wizard-body .wizard-content .accordion .panel-group .panel .panel-heading .panel-title > i {
+ font-size: 20px;
+ color: #1491c1;
+}
+.accordion .panel-group .panel .panel-heading:hover,
+.wizard .wizard-body .wizard-content .accordion .panel-group .panel .panel-heading:hover {
+ background: #f3faff;
+ cursor: pointer;
+}
+.accordion .panel-group .panel .panel-body,
+.wizard .wizard-body .wizard-content .accordion .panel-group .panel .panel-body {
+ padding: 15px 10px 20px 20px;
+}
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6 {
+ font-family: 'Roboto', sans-serif;
+}
+h1,
+.h1 {
+ font-size: 24px;
+}
+h2,
+.h2 {
+ font-size: 18px;
+}
+body,
+.body {
+ font-family: 'Roboto', sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ line-height: 1;
+ color: #333;
+ font-size: 14px;
+}
+.description {
+ font-family: 'Roboto', sans-serif;
+ font-size: 12px;
+ color: #000;
+}
+a,
+a:visited,
+a:focus {
+ color: #1491C1;
+ text-decoration: none;
+}
+a:hover,
+a:visited:hover,
+a:focus:hover {
+ text-decoration: underline;
+}
+a:active,
+a:visited:active,
+a:focus:active {
+ text-decoration: none;
+}
+a[disabled],
+a:visited[disabled],
+a:focus[disabled],
+a.disabled,
+a:visited.disabled,
+a:focus.disabled {
+ cursor: not-allowed;
+ color: #666;
+ text-decoration: none;
+}
+a[disabled]:hover,
+a:visited[disabled]:hover,
+a:focus[disabled]:hover,
+a.disabled:hover,
+a:visited.disabled:hover,
+a:focus.disabled:hover {
+ text-decoration: none;
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/bootstrap-hadoop.min.css b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/bootstrap-hadoop.min.css
new file mode 100644
index 0000000..ea438a9
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/bootstrap-hadoop.min.css
@@ -0,0 +1,18 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@font-face{font-family:Roboto;font-weight:400;font-style:normal;src:url(fonts/Roboto-Regular-webfont.eot);src:url(fonts/Roboto-Regular-webfont.eot?#iefix) format('embedded-opentype'),url(fonts/Roboto-Regular-webfont.woff) format('woff'),url(fonts/Roboto-Regular-webfont.ttf) format('truetype'),url(fonts/Roboto-Regular-webfont.svg#robotoregular) format('svg')}.font-mixin{font-family:Roboto,sans-serif;font-weight:400;font-style:normal;line-height:1;color:#333}.btn,.btn:focus{outline:0;font- [...]
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/img/feather.png b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/img/feather.png
new file mode 100644
index 0000000..d4aff8b
Binary files /dev/null and b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/img/feather.png differ
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/img/jenkins.png b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/img/jenkins.png
new file mode 100644
index 0000000..7b332ec
Binary files /dev/null and b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/img/jenkins.png differ
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/img/loading.svg b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/img/loading.svg
new file mode 100644
index 0000000..fcdb87d
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/img/loading.svg
@@ -0,0 +1,8 @@
+<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-dual-ring" style="background: none;">
+ <circle cx="50" cy="50" ng-attr-r="{{config.radius}}" ng-attr-stroke-width="{{config.width}}" ng-attr-stroke="{{config.c1}}" ng-attr-stroke-dasharray="{{config.dasharray}}" fill="none" stroke-linecap="round" r="40" stroke-width="4" stroke="#84de5c" stroke-dasharray="62.83185307179586 62.83185307179586" transform="rotate(318 50 50)">
+ <animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform>
+ </circle>
+ <circle cx="50" cy="50" ng-attr-r="{{config.radius2}}" ng-attr-stroke-width="{{config.width}}" ng-attr-stroke="{{config.c2}}" ng-attr-stroke-dasharray="{{config.dasharray2}}" ng-attr-stroke-dashoffset="{{config.dashoffset2}}" fill="none" stroke-linecap="round" r="35" stroke-width="4" stroke="#21d129" stroke-dasharray="54.97787143782138 54.97787143782138" stroke-dashoffset="54.97787143782138" transform="rotate(-318 50 50)">
+ <animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;-360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform>
+ </circle>
+ </svg>
\ No newline at end of file
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/img/pattern-f61c2e99f82389a67432f54155c5f483.png b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/img/pattern-f61c2e99f82389a67432f54155c5f483.png
new file mode 100644
index 0000000..2d62a14
Binary files /dev/null and b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/img/pattern-f61c2e99f82389a67432f54155c5f483.png differ
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/specific.css b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/specific.css
new file mode 100644
index 0000000..3e75f91
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/specific.css
@@ -0,0 +1,252 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.tile {
+ margin: 5px 5px 0px 5px;
+ padding: 5px;
+ border: 1px solid #cdcdcd;
+ height: 210px;
+ overflow: hidden;
+ background-color: #ffffff;
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ min-width: 250px;
+}
+
+.panel {
+ margin: 5px 0px 0px 0px;
+ padding: 5px;
+ border: 1px solid #cdcdcd;
+}
+
+.desc {
+ position: relative;
+ top: 100px;
+ margin: 5px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ height: 60px;
+ padding-right: 25px;
+}
+
+.appBanner {
+ display: block;
+ height: 40px;
+}
+
+.appList {
+ display: block;
+}
+
+html, body, .container {
+ height: 100%;
+}
+
+.container {
+ display:table;
+ width: 100%;
+ margin: 0;
+ padding: 0; /*set left/right padding according to needs*/
+ box-sizing: border-box;
+}
+
+.row {
+ height: 100%;
+ display: table-row;
+}
+
+.wizard {
+ height: 100%;
+ border: none;
+}
+
+.wizard-body {
+ height: 100%;
+}
+
+.navigation-header div.btn-group div {
+ display: inline;
+}
+
+.navigation-header div.btn-group div a:hover {
+ text-decoration: none;
+}
+
+.navigation-bar-container ul.nav.side-nav-menu li.mainmenu-li {
+ color: #999;
+}
+
+.navigation-bar-container ul.nav.side-nav-menu li.mainmenu-li:hover {
+ background-color: #404351;
+}
+
+.navigation-menu-item {
+ padding-left: 30px;
+}
+
+ul.nav.side-nav-menu li {
+ height: 55px;
+}
+
+.navigation-bar-container ul.nav.side-nav-menu li.mainmenu-li>a {
+ color: #b8bec4;
+}
+
+.navigation-bar {
+ height: 100%;
+}
+
+.navigation-bar-container {
+ height: 100%;
+}
+
+.content {
+ margin: 5px;
+}
+
+.mdl-shadow-4dp {
+ box-shadow: 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12), 0 2px 4px -1px rgba(0,0,0,.2);
+}
+
+/*
+ * hexagon
+ */
+.hexa, .hexa div {
+ position: absolute;
+ top: -5px;
+ left: 0px;
+ overflow: hidden;
+ width: 128px;
+ height: 128px;
+}
+.hexa {
+ width: 128px;
+ height: 96px;
+}
+.hexa div {
+ width: 100%;
+ height: 100%;
+}
+.hexa {
+ -webkit-transform: rotate(120deg);
+ -moz-transform: rotate(120deg);
+ -ms-transform: rotate(120deg);
+ -o-transform: rotate(120deg);
+ transform: rotate(120deg);
+}
+.hex1 {
+ -webkit-transform: rotate(-60deg);
+ -moz-transform: rotate(-60deg);
+ -ms-transform: rotate(-60deg);
+ -o-transform: rotate(-60deg);
+ transform: rotate(-60deg);
+}
+.hex2 {
+ box-shadow: 0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12), 0 2px 4px -1px rgba(0,0,0,.2);
+ -webkit-transform: rotate(-60deg);
+ -moz-transform: rotate(-60deg);
+ -ms-transform: rotate(-60deg);
+ -o-transform: rotate(-60deg);
+ transform: rotate(-60deg);
+}
+
+/*
+ * card title
+ */
+.card-title {
+ position: absolute;
+ top: 5px;
+ left: 128px;
+}
+
+.card-desc {
+ margin: 10px;
+ padding-right: 25px;
+}
+
+.card-footer {
+ position: relative;
+ margin: 0px 5px 5px 5px;
+ padding: 5px;
+ background-color: #eeeeee;
+ line-height: 25px;
+ height: 45px;
+ color: #666;
+ font-family: Roboto,sans-serif!important;
+ font-size: 14;
+ min-width: 250px;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+}
+
+.toparea {
+ position: fixed;
+ top: 0px;
+ left: 230px;
+ width: calc(100% - 230px);
+ height: 300px;
+ color: #2c2e3b;
+ background: -webkit-linear-gradient(left,#2e3b51 ,#181d2b);
+ background: -moz-linear-gradient(right,#2e3b51,#181d2b);
+ background: -o-linear-gradient(right,#2e3b51,#181d2b);
+ background: linear-gradient(to right,#2e3b51 ,#181d2b);
+}
+
+.pattern-wrapper {
+ width: 100%;
+ height: 300px;
+ background-image: url(img/pattern-f61c2e99f82389a67432f54155c5f483.png);
+ background-repeat: no-repeat;
+ background-position: 0px 30px;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.recommended {
+ color: #fff;
+}
+
+.loader-wrapper {
+ position: fixed;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background: #222222;
+ z-index: 1000;
+ opacity: 0.3;
+}
+
+.loader {
+ display: table;
+ margin: 0 auto;
+ position: relative;
+ top: 50%;
+ transform: translateY(-50%);
+}
+
+.loader img {
+ width: 300px;
+ height: 225px;
+}
+
+.infobox {
+ font-size:14px;
+ margin:20px;
+ padding:10px;
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/theme.css b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/theme.css
new file mode 100644
index 0000000..e787efe
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/css/theme.css
@@ -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.
+ */
+.theme-dropdown .dropdown-menu {
+ position: static;
+ display: block;
+ margin-bottom: 20px;
+}
+
+.theme-showcase > p > .btn {
+ margin: 5px 0;
+}
+
+.theme-showcase .navbar .container {
+ width: auto;
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/index.html b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/index.html
new file mode 100644
index 0000000..2a96027
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/index.html
@@ -0,0 +1,82 @@
+<!---
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. See accompanying LICENSE file.
+-->
+<!doctype html>
+<html lang="en" data-ng-app="app">
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <title>YARN Application Catalog</title>
+ <link rel="stylesheet" href="vendor/bootstrap/dist/css/bootstrap.min.css">
+ <link rel="stylesheet" href="vendor/bootstrap/dist/css/bootstrap-theme.min.css">
+ <link href="css/bootstrap-hadoop.css" rel="stylesheet">
+ <link rel="stylesheet" href="css/theme.css">
+ <link rel="stylesheet" href="css/specific.css">
+</head>
+<body role="document">
+<div ng-controller="LoadScreenController" class="loader-wrapper {{loadScreen}}">
+ <div class="loader"><img src="/css/img/loading.svg"></div>
+</div>
+<div class="container">
+ <div class="row">
+ <div class="navigation-bar-container col-sm-3 col-md-3 col-lg-3" ng-controller="AppListController">
+ <ul class="side-nav-header nav nav-pills nav-stacked">
+ <li class="navigation-header">
+ <div class="btn-group">
+ <div>
+ <a href="#"><span class="ambari-header">Applications</span></a>
+ </div>
+ <div class="dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+ <span class="toggle-icon glyphicon glyphicon-menu-down"></span>
+ </div>
+ <ul class="dropdown-menu">
+ <li><a href="#!/">Appstore</a></li>
+ <li><a href="#!/new">Register App</a></li>
+ </ul>
+ </div>
+ </li>
+ </ul>
+ <ul class="side-nav-menu nav nav-pills nav-stacked">
+ <li class="mainmenu-li" ng-repeat="yarnApp in appList">
+ <a href="#!/app/{{yarnApp.id}}"><span class="glyphicon glyphicon-ok-sign"></span> {{yarnApp.name}}</a>
+ <a ng-click="deleteApp(yarnApp.id,yarnApp.name)" class="icon-width"><span class="glyphicon glyphicon-remove pull-right"></span></a>
+ <span class="navigation-menu-item">{{yarnApp.app}}</span>
+ </li>
+ </ul>
+ <ul class="side-nav-footer nav nav-pills nav-stacked">
+ <li class="navigation-footer">
+ <a href="#" data-toggle="collapse-side-nav">
+ <span class="navigation-icon fa fa-angle-double-left"></span>
+ </a>
+ </li>
+ </ul>
+ </div>
+
+ <div class="col-xs-9 col-md-9 col-lg-9">
+ <div data-ng-view></div>
+ </div>
+ </div>
+</div>
+<script src="vendor/jquery/dist/jquery.min.js"></script>
+<script src="vendor/bootstrap/dist/js/bootstrap.min.js"></script>
+<script src="vendor/angular/angular.min.js"></script>
+<script src="vendor/angular-route/angular-route.min.js"></script>
+<script src="js/bootstrap-hadoop.min.js"></script>
+<script src="js/app.js"></script>
+<script src="js/controllers.js"></script>
+<script src="js/filters.js"></script>
+<script src="js/routes.js"></script>
+<script src="js/services.js"></script>
+</body>
+</html>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/js/bootstrap-hadoop.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/js/bootstrap-hadoop.js
new file mode 100644
index 0000000..bd57d5c
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/js/bootstrap-hadoop.js
@@ -0,0 +1,284 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+'use strict';
+
+$(document).ready(function () {
+ var $accordionToggler = $(this).find('[data-toggle="collapseAccordion"]');
+ $accordionToggler.off('click').on('click', function (event) {
+ var $this = $(this);
+ $this.siblings('.panel-body').slideToggle(500);
+ $this.children().children('.panel-toggle').toggleClass('fa-angle-down fa-angle-up');
+ event.stopPropagation();
+ return false;
+ });
+});
+'use strict';
+
+(function ($) {
+
+ /**
+ * jQuery plugin for navigation bars
+ * Usage:
+ * <pre>
+ * $('.navigation-bar').navigationBar();
+ * </pre>
+ *
+ * @param {object} options see <code>$.fn.navigationBar.defaults</code>
+ * @returns {$}
+ */
+
+ $.fn.navigationBar = function (options) {
+
+ var settings = $.extend({}, $.fn.navigationBar.defaults, options);
+
+ return this.each(function () {
+ var _this = this;
+
+ var containerSelector = '.navigation-bar-container';
+ var $navigationContainer = $(this).find(containerSelector);
+ var $sideNavToggler = $(this).find('[data-toggle=' + settings.navBarToggleDataAttr + ']');
+ var $subMenuToggler = $(this).find('[data-toggle=' + settings.subMenuNavToggleDataAttr + ']');
+ var firstLvlMenuItemsSelector = '.side-nav-menu>li';
+ var secondLvlMenuItemsSelector = '.side-nav-menu>li>ul>li';
+ var $moreActions = $(this).find('.more-actions');
+ var $dropdownMenu = $moreActions.children('.dropdown-menu');
+
+ $subMenuToggler.each(function (index, toggler) {
+ return $(toggler).parent().addClass('has-sub-menu');
+ });
+
+ if (settings.fitHeight) {
+ $(this).addClass('navigation-bar-fit-height');
+
+ // make scrolling effect on side nav ONLY, i.e. not effected on ambari main contents
+ $(this).find('.side-nav-menu').on('DOMMouseScroll mousewheel', function (ev) {
+ var $this = $(this),
+ scrollTop = this.scrollTop,
+ scrollHeight = this.scrollHeight,
+ height = $this.innerHeight(),
+ delta = ev.originalEvent.wheelDelta,
+ up = delta > 0;
+ var prevent = function prevent() {
+ ev.stopPropagation();
+ ev.preventDefault();
+ ev.returnValue = false;
+ return false;
+ };
+
+ if (!up && -delta > scrollHeight - height - scrollTop) {
+ // Scrolling down, but this will take us past the bottom.
+ $this.scrollTop(scrollHeight);
+ return prevent();
+ } else if (up && delta > scrollTop) {
+ // Scrolling up, but this will take us past the top.
+ $this.scrollTop(0);
+ return prevent();
+ }
+ });
+ }
+
+ //set main content left margin based on the width of side-nav
+ var containerWidth = $navigationContainer.width();
+ if (settings.moveLeftContent) {
+ $(settings.content).css('margin-left', containerWidth);
+ }
+ if (settings.moveLeftFooter) {
+ $(settings.footer).css('margin-left', containerWidth);
+ }
+
+ function popStateHandler() {
+ var path = window.location.pathname + window.location.hash;
+ $navigationContainer.find('li a').each(function (index, link) {
+ var $link = $(link);
+ var href = $link.attr('data-href') || $link.attr('href');
+ if (path.indexOf(href) !== -1 && ['', '#'].indexOf(href) === -1) {
+ $link.parent().addClass('active');
+ } else {
+ $link.parent().removeClass('active');
+ }
+ });
+ }
+
+ if (settings.handlePopState) {
+ popStateHandler();
+ $(window).bind('popstate', popStateHandler);
+ }
+
+ function clickHandler(el) {
+ var $li = $(el).parent();
+ var activeClass = settings.activeClass;
+
+ var activeMenuItems = firstLvlMenuItemsSelector + '.' + activeClass;
+ var activeSubMenuItems = secondLvlMenuItemsSelector + '.' + activeClass;
+ $navigationContainer.find(activeMenuItems).removeClass(activeClass);
+ $navigationContainer.find(activeSubMenuItems).removeClass(activeClass);
+ $li.addClass(activeClass);
+ }
+
+ /**
+ * Click on menu item
+ */
+ $(firstLvlMenuItemsSelector + '>a').on('click', function () {
+ clickHandler(this);
+ });
+
+ /**
+ * Click on sub menu item
+ */
+ $(secondLvlMenuItemsSelector + '>a').on('click', function () {
+ clickHandler(this);
+ $(this).parent().parent().parent().addClass(settings.activeClass);
+ });
+
+ /**
+ * Slider for sub menu
+ */
+ $subMenuToggler.off('click').on('click', function (event) {
+ // ignore click if navigation-bar is collapsed
+ if ($navigationContainer.hasClass('collapsed')) {
+ return false;
+ }
+ var $this = $(this);
+ $this.siblings('.sub-menu').slideToggle(600, function () {
+ var $topMenuItem = $this.parent();
+ var $subMenu = $topMenuItem.find('ul');
+ return $subMenu.is(':visible') ? $topMenuItem.removeClass('collapsed') : $topMenuItem.addClass('collapsed');
+ });
+ $this.children('.toggle-icon').toggleClass(settings.menuLeftClass + ' ' + settings.menuDownClass);
+ event.stopPropagation();
+ return false;
+ });
+
+ /**
+ * Hovering effects for "more actions icon": "..."
+ */
+ $(this).find('.mainmenu-li>a').hover(function () {
+ var $moreIcon = $(this).siblings('.more-actions');
+ if ($moreIcon.length && !$navigationContainer.hasClass('collapsed')) {
+ $moreIcon.css('display', 'inline-block');
+ }
+ }, function () {
+ var $moreIcon = $(this).siblings('.more-actions');
+ if ($moreIcon.length && !$navigationContainer.hasClass('collapsed')) {
+ $moreIcon.hide();
+ }
+ });
+ $moreActions.hover(function () {
+ $(this).css('display', 'inline-block');
+ });
+ if (settings.fitHeight) {
+ $moreActions.on('click', function () {
+ // set actions submenu position
+ var $moreIcon = $(this);
+ var $header = $('.side-nav-header');
+ $dropdownMenu.css({
+ top: $moreIcon.offset().top - $header.offset().top + 20 + 'px',
+ left: $moreIcon.offset().left + 'px'
+ });
+ });
+ }
+ $dropdownMenu.on('click', function () {
+ // some action was triggered, should hide this icon
+ var moreIcon = $(this).parent();
+ setTimeout(function () {
+ moreIcon.hide();
+ }, 1000);
+ });
+ $navigationContainer.children('.side-nav-menu').scroll(function () {
+ $moreActions.removeClass('open');
+ });
+
+ /**
+ * Expand/collapse navigation bar
+ */
+ $sideNavToggler.click(function () {
+
+ $navigationContainer.toggleClass('collapsed').promise().done(function () {
+ var subMenuSelector = 'ul.sub-menu';
+ var $subMenus = $navigationContainer.find(subMenuSelector);
+ var $subMenuItems = $navigationContainer.find('.side-nav-menu>li');
+ if ($navigationContainer.hasClass('collapsed')) {
+ // set sub menu invisible when collapsed
+ $subMenus.hide();
+ $moreActions.hide();
+ // set the hover effect when collapsed, should show sub-menu on hovering
+ $subMenuItems.hover(function () {
+ $(this).find(subMenuSelector).show();
+ // set sub-menu position
+ var $parent = $(this);
+ var $header = $('.side-nav-header');
+ if (settings.fitHeight) {
+ $(this).find(subMenuSelector).css({
+ position: 'fixed',
+ top: $parent.offset().top - $header.offset().top + 'px',
+ left: 50 + 'px'
+ });
+ }
+ }, function () {
+ $(this).find(subMenuSelector).hide();
+ });
+ } else {
+ // keep showing all sub menu
+ $subMenus.show().each(function (index, item) {
+ return $(item).parent().removeClass('collapsed');
+ });
+ $subMenuItems.unbind('mouseenter mouseleave');
+ $navigationContainer.find('.toggle-icon').removeClass(settings.menuLeftClass).addClass(settings.menuDownClass);
+ // set sub-menu position
+ if (settings.fitHeight) {
+ $(_this).find(subMenuSelector).css({
+ position: 'relative',
+ top: 0,
+ left: 0
+ });
+ }
+ }
+
+ $navigationContainer.on('transitionend', function () {
+ //set main content left margin based on the width of side-nav
+ var containerWidth = $navigationContainer.width();
+ if (settings.moveLeftContent) {
+ $(settings.content).css('margin-left', containerWidth);
+ }
+ if (settings.moveLeftFooter) {
+ $(settings.footer).css('margin-left', containerWidth);
+ }
+ });
+ $sideNavToggler.find('span').toggleClass(settings.collapseNavBarClass + ' ' + settings.expandNavBarClass);
+ });
+ return false;
+ });
+ });
+ };
+
+ $.fn.navigationBar.defaults = {
+ handlePopState: true,
+ fitHeight: false,
+ content: '#main',
+ footer: 'footer',
+ moveLeftContent: true,
+ moveLeftFooter: true,
+ menuLeftClass: 'glyphicon-menu-right',
+ menuDownClass: 'glyphicon-menu-down',
+ collapseNavBarClass: 'fa-angle-double-left',
+ expandNavBarClass: 'fa-angle-double-right',
+ activeClass: 'active',
+ navBarToggleDataAttr: 'collapse-side-nav',
+ subMenuNavToggleDataAttr: 'collapse-sub-menu'
+ };
+})(jQuery);
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/js/bootstrap-hadoop.min.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/js/bootstrap-hadoop.min.js
new file mode 100644
index 0000000..6872799
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/js/bootstrap-hadoop.min.js
@@ -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.
+ */
+"use strict";$(document).ready(function(){$(this).find('[data-toggle="collapseAccordion"]').off("click").on("click",function(n){var l=$(this);return l.siblings(".panel-body").slideToggle(500),l.children().children(".panel-toggle").toggleClass("fa-angle-down fa-angle-up"),n.stopPropagation(),!1})});
+"use strict";!function(e){e.fn.navigationBar=function(n){var t=e.extend({},e.fn.navigationBar.defaults,n);return this.each(function(){function n(){var n=window.location.pathname+window.location.hash;i.find("li a").each(function(t,a){var s=e(a),i=s.attr("data-href")||s.attr("href");-1!==n.indexOf(i)&&-1===["","#"].indexOf(i)?s.parent().addClass("active"):s.parent().removeClass("active")})}function a(n){var a=e(n).parent(),s=t.activeClass,o=r+"."+s,l=f+"."+s;i.find(o).removeClass(s),i.find [...]
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/deploy.html b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/deploy.html
new file mode 100644
index 0000000..534d0e9
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/deploy.html
@@ -0,0 +1,80 @@
+<!---
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. See accompanying LICENSE file.
+-->
+<div class="container content">
+ <div class="row">
+ <div class="col-xs-12 col-md-12 col-lg-12">
+ <div class="form-group">
+ <h1>Deploy Application</h1>
+ </div>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="col-xs-2 col-md-2 col-lg-2">
+ <img ng-src="{{details.icon}}" alt="" width="128" height="128" ng-if="details.icon != null" />
+ </div>
+ <div class="col-xs-10 col-md-10 col-lg-10">
+ <div class="form-group">
+ <h2>{{details.org}}/{{details.name}}</h2>
+ {{details.desc}}
+ </div>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="col-xs-12 col-md-12 col-lg-12">
+ <div class="form-group">
+ <label>Application Name</label>
+ <input type=text name="name" class="form-control" ng-model="details.app.name" autofocus />
+ </div>
+ <div class="form-group">
+ <label>Queue</label>
+ <input type=text name="queue" class="form-control" ng-model="details.app.queue" />
+ </div>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="col-xs-12 col-md-12 col-lg-12" ng-repeat="docker in details.app.components track by $index">
+ <div class="panel">
+ <div class="form-group">
+ <label>Component {{docker.name}}</label>
+ </div>
+ <div class="form-group">
+ <label>Number of containers</label>
+ <input type=text name="artifact_id" class="form-control" ng-model="docker.number_of_containers" />
+ </div>
+ <div class="form-group">
+ <label>Environments</label>
+ <textarea json-text name="env" class="form-control" ng-model="docker.configuration.env"/></textarea>
+ </div>
+ <div class="form-group">
+ <label>Properties</label>
+ <textarea json-text name="properties" class="form-control" ng-model="docker.configuration.properties"/></textarea>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <br>
+ <div class="row">
+ <div class="col-xs-12 col-md-12 col-lg-12">
+ <p>
+ <a class="btn btn-secondary" ng-click="launchApp(details.app)">LAUNCH</a>
+ <a class="btn btn-secondary" href="/">CANCEL</a>
+ <span class="infobox bg-info" ng-if="message">{{message}}</span>
+ <span class="infobox bg-danger" ng-if="error">{{error}}</span>
+ </div>
+ </div>
+</div>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/details.html b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/details.html
new file mode 100644
index 0000000..69ef912
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/details.html
@@ -0,0 +1,64 @@
+<!---
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. See accompanying LICENSE file.
+-->
+<div class="container content">
+ <div class="row">
+ <div class="col-xs-12 col-md-12 col-lg-12">
+ <a ng-click="stopApp(appName)" class="btn btn-secondary"><span class="glyphicon glyphicon-stop"></span> Stop</a>
+ <a ng-click="restartApp(appName)" class="btn btn-secondary"><span class="glyphicon glyphicon-refresh"></span> Start</a>
+ <div style="display:inline-block;" ng-repeat="(key, value) in details.yarnfile.quicklinks">
+ <a href="{{value}}" class="btn btn-secondary" ng-hide="checkServiceLink()"><span class="glyphicon glyphicon-new-window"></span> {{key}}</a>
+ </div>
+ {{details.yarnfile.state}}
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="col-xs-12 col-md-12 col-lg-12" ng-repeat="docker in details.yarnfile.components">
+ <div class="panel">
+ <h3>{{docker.name}}</h3>
+ <p>Artifact: {{docker.artifact.id}}</p>
+ <p>Number of containers: {{docker.number_of_containers}}</p>
+ <p>CPU: {{docker.resource.cpus}}</p>
+ <p>Memory: {{docker.resource.memory}}</p>
+ <p>Environments
+ <table class="table">
+ <tbody class="table-striped">
+ <tr ng-repeat="(key, value) in docker.configuration.env">
+ <td>{{key}}</td><td>{{value}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </p>
+ <table class="table">
+ <thead>
+ <tr>
+ <th>Hostname</th>
+ <th>Container ID</th>
+ <th>Launch Time</th>
+ <th>State</th>
+ </tr>
+ </thead>
+ <tbody class="table-striped">
+ <tr ng-repeat="container in docker.containers">
+ <td>{{container.bare_host}}</td>
+ <td><a href="http://{{container.bare_host}}:8042/terminal/terminal.template?container={{container.id}}" target="_blank">{{container.id}}</a></td>
+ <td>{{container.launch_time | date:'yyyy-MM-dd HH:mm:ss Z'}}</td>
+ <td>{{container.state}}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/home.html b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/home.html
new file mode 100644
index 0000000..e2b7f08
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/home.html
@@ -0,0 +1,52 @@
+<!---
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. See accompanying LICENSE file.
+-->
+<div class="toparea"><div class="pattern-wrapper"></div></div>
+<div class="container content toparea pattern-wrapper">
+ <form>
+ <div class="input-group input-group-lg">
+ <div class="input-group-addon">
+ <span class="glyphicon glyphicon-search" id="search"></span>
+ </div>
+ <input type="text" class="form-control" placeholder="Search for applications from HDFS" aria-describedby="search" ng-model="searchText" ng-change="change(text)" />
+ </div>
+ </form>
+
+ <p></p>
+ <p class="recommended">Recommended</p>
+
+ <div ng-repeat="a in appStore" ng-if="$index % 3 == 0" class="row">
+ <div ng-repeat="i in [$index, $index + 1, $index + 2]"
+ ng-if="appStore[i] != null" class="col-xs-4 col-md-4 col-lg-4">
+ <div class="tile mdl-shadow-4dp">
+ <img ng-src="{{appStore[i].icon}}" alt="" width="128" height="128" ng-if="appStore[i] != null" />
+ <div class="card-title">
+ <h3>{{appStore[i].org}}</h3>
+ <h2>{{appStore[i].name}}</h2>
+ </div>
+ <div class="card-desc">
+ <p>{{appStore[i].desc}}</p>
+ </div>
+ </div>
+ <div class="card-footer mdl-shadow-4dp">
+ <div class="pull-left">
+ <!-- <span class="glyphicon glyphicon-thumbs-up"></span> {{appStore[i].like | counterValue}} -->
+ <span class="glyphicon glyphicon-cloud-download"></span> {{appStore[i].download | counterValue}}
+ </div>
+ <div class="pull-right">
+ <button class="btn btn-secondary" ng-click="deployApp(appStore[i].id)">DEPLOY</button>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/new.html b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/new.html
new file mode 100644
index 0000000..4017225
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/partials/new.html
@@ -0,0 +1,111 @@
+<!---
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. See accompanying LICENSE file.
+-->
+<div class="container content">
+ <div class="row">
+ <div class="col-xs-12 col-md-12 col-lg-12">
+ <div class="form-group">
+ <h1>Register Application</h1>
+ </div>
+ <div class="form-group">
+ <label>Organization Name</label>
+ <input type=text name="org" class="form-control" ng-model="details.organization" />
+ </div>
+ <div class="form-group">
+ <label>Application Name</label>
+ <input type=text name="name" class="form-control" ng-model="details.name" />
+ </div>
+ <div class="form-group">
+ <label>Version</label>
+ <input type=text name="version" class="form-control" ng-model="details.version" />
+ </div>
+ <div class="form-group">
+ <label>Description</label>
+ <textarea name="desc" class="form-control" ng-model="details.description"></textarea>
+ </div>
+ <div class="form-group">
+ <label>Quick Link</label>
+ <textarea json-text name="quicklink" class="form-control" ng-model="details.quicklinks"/></textarea>
+ </div>
+ <div class="form-group">
+ <label>Icon</label>
+ <input type=text name="icon" class="form-control" ng-model="details.icon" />
+ </div>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="col-xs-12 col-md-12 col-lg-12" ng-repeat="docker in details.components track by $index">
+ <div class="panel">
+ <div class="pull-right">
+ <a class="btn btn-secondary" ng-click="remove($index)">-</a>
+ </div>
+ <div class="form-group">
+ <label>Component Name</label>
+ <input type=text name="name" class="form-control" ng-model="docker.name" />
+ </div>
+ <div class="form-group">
+ <label>Artifact</label>
+ <input type=text name="artifact_id" class="form-control" ng-model="docker.artifact.id" />
+ </div>
+ <div class="form-group">
+ <label>Number of containers</label>
+ <input type=text name="artifact_id" class="form-control" ng-model="docker.number_of_containers" />
+ </div>
+ <div class="form-group">
+ <label>Launch Command</label>
+ <input type=text name="launch_command" class="form-control" ng-model="docker.launch_command" />
+ </div>
+ <div class="form-group">
+ <label>CPU</label>
+ <input type=text name="cpus" class="form-control" ng-model="docker.resource.cpus" />
+ </div>
+ <div class="form-group">
+ <label>Memory</label>
+ <input type=text name="memory" class="form-control" ng-model="docker.resource.memory" />
+ </div>
+ <div class="form-group">
+ <input type="checkbox" ng-attr-id="{{'checkbox-priv-' + $index}}" ng-model="docker.run_privileged_container">
+ <label for="checkbox-priv-{{$index}}"> Privileged Container</label>
+ </div>
+ <div class="form-group">
+ <label>Dependencies</label>
+ <input json-text type=text name="dependencies" class="form-control" ng-model="docker.dependencies" />
+ </div>
+ <div class="form-group">
+ <label>Placement Policy</label>
+ <input type=text name="placement" class="form-control" ng-model="docker.placement_policy.constraints" />
+ </div>
+ <div class="form-group">
+ <label>Environments</label>
+ <textarea json-text name="env" class="form-control" ng-model="docker.configuration.env"/></textarea>
+ </div>
+ <div class="form-group">
+ <label>Properties</label>
+ <textarea json-text name="properties" class="form-control" ng-model="docker.configuration.properties"/></textarea>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <br>
+ <div class="row">
+ <div class="col-xs-12 col-md-12 col-lg-12">
+ <p>
+ <a class="btn btn-secondary" ng-click="add()">+</a>
+ <a class="btn btn-secondary" ng-click="save()">Publish</a>
+ <span class="infobox bg-info" ng-if="message">{{message}}</span>
+ <span class="infobox bg-danger" ng-if="error">{{error}}</span>
+ </div>
+ </div>
+</div>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/theme.html b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/theme.html
new file mode 100644
index 0000000..a8a2a2d
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/main/webapp/theme.html
@@ -0,0 +1,649 @@
+<!---
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License. See accompanying LICENSE file.
+-->
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="description" content="">
+ <meta name="author" content="">
+
+ <title>Theme Template for Bootstrap</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="vendor/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
+ <!-- Bootstrap theme -->
+ <link href="vendor/bootstrap/dist/css/bootstrap-theme.min.css" rel="stylesheet">
+
+ <!-- Custom styles for this template -->
+ <link href="css/theme.css" rel="stylesheet">
+ <link href="css/bootstrap-hadoop.min.css" rel="stylesheet">
+
+</head>
+
+<body role="document">
+
+<!-- Fixed navbar -->
+<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
+ <div class="container">
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a class="navbar-brand" href="#">Bootstrap theme</a>
+ </div>
+ <div class="navbar-collapse collapse">
+ <ul class="nav navbar-nav">
+ <li class="active"><a href="index.html">Home</a></li>
+ <li><a href="#about">About</a></li>
+ <li><a href="#contact">Contact</a></li>
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <span class="caret"></span></a>
+ <ul class="dropdown-menu" role="menu">
+ <li><a href="#">Action</a></li>
+ <li><a href="#">Another action</a></li>
+ <li><a href="#">Something else here</a></li>
+ <li class="divider"></li>
+ <li class="dropdown-header">Nav header</li>
+ <li><a href="#">Separated link</a></li>
+ <li><a href="#">One more separated link</a></li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <!--/.nav-collapse -->
+ </div>
+</div>
+
+<div class="container theme-showcase" role="main">
+
+<!-- Main jumbotron for a primary marketing message or call to action -->
+<div class="jumbotron">
+ <h1>Hello, world!</h1>
+
+ <p>This is a template for a simple marketing or informational website. It includes a large callout called a
+ jumbotron and three supporting pieces of content. Use it as a starting point to create something more
+ unique.</p>
+
+ <p><a href="#" class="btn btn-primary btn-lg" role="button">Learn more »</a></p>
+</div>
+
+
+<div class="page-header">
+ <h1>Buttons</h1>
+</div>
+<p>
+ <button type="button" class="btn btn-lg btn-default">Default</button>
+ <button type="button" class="btn btn-lg btn-primary">Primary</button>
+ <button type="button" class="btn btn-lg btn-success">Success</button>
+ <button type="button" class="btn btn-lg btn-info">Info</button>
+ <button type="button" class="btn btn-lg btn-warning">Warning</button>
+ <button type="button" class="btn btn-lg btn-danger">Danger</button>
+ <button type="button" class="btn btn-lg btn-link">Link</button>
+</p>
+<p>
+ <button type="button" class="btn btn-default">Default</button>
+ <button type="button" class="btn btn-primary">Primary</button>
+ <button type="button" class="btn btn-success">Success</button>
+ <button type="button" class="btn btn-info">Info</button>
+ <button type="button" class="btn btn-warning">Warning</button>
+ <button type="button" class="btn btn-danger">Danger</button>
+ <button type="button" class="btn btn-link">Link</button>
+</p>
+<p>
+ <button type="button" class="btn btn-sm btn-default">Default</button>
+ <button type="button" class="btn btn-sm btn-primary">Primary</button>
+ <button type="button" class="btn btn-sm btn-success">Success</button>
+ <button type="button" class="btn btn-sm btn-info">Info</button>
+ <button type="button" class="btn btn-sm btn-warning">Warning</button>
+ <button type="button" class="btn btn-sm btn-danger">Danger</button>
+ <button type="button" class="btn btn-sm btn-link">Link</button>
+</p>
+<p>
+ <button type="button" class="btn btn-xs btn-default">Default</button>
+ <button type="button" class="btn btn-xs btn-primary">Primary</button>
+ <button type="button" class="btn btn-xs btn-success">Success</button>
+ <button type="button" class="btn btn-xs btn-info">Info</button>
+ <button type="button" class="btn btn-xs btn-warning">Warning</button>
+ <button type="button" class="btn btn-xs btn-danger">Danger</button>
+ <button type="button" class="btn btn-xs btn-link">Link</button>
+</p>
+
+
+<div class="page-header">
+ <h1>Tables</h1>
+</div>
+<div class="row">
+ <div class="col-md-6">
+ <table class="table">
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>First Name</th>
+ <th>Last Name</th>
+ <th>Username</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>Mark</td>
+ <td>Otto</td>
+ <td>@mdo</td>
+ </tr>
+ <tr>
+ <td>2</td>
+ <td>Jacob</td>
+ <td>Thornton</td>
+ <td>@fat</td>
+ </tr>
+ <tr>
+ <td>3</td>
+ <td>Larry</td>
+ <td>the Bird</td>
+ <td>@twitter</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div class="col-md-6">
+ <table class="table table-striped">
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>First Name</th>
+ <th>Last Name</th>
+ <th>Username</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>Mark</td>
+ <td>Otto</td>
+ <td>@mdo</td>
+ </tr>
+ <tr>
+ <td>2</td>
+ <td>Jacob</td>
+ <td>Thornton</td>
+ <td>@fat</td>
+ </tr>
+ <tr>
+ <td>3</td>
+ <td>Larry</td>
+ <td>the Bird</td>
+ <td>@twitter</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</div>
+
+<div class="row">
+ <div class="col-md-6">
+ <table class="table table-bordered">
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>First Name</th>
+ <th>Last Name</th>
+ <th>Username</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td rowspan="2">1</td>
+ <td>Mark</td>
+ <td>Otto</td>
+ <td>@mdo</td>
+ </tr>
+ <tr>
+ <td>Mark</td>
+ <td>Otto</td>
+ <td>@TwBootstrap</td>
+ </tr>
+ <tr>
+ <td>2</td>
+ <td>Jacob</td>
+ <td>Thornton</td>
+ <td>@fat</td>
+ </tr>
+ <tr>
+ <td>3</td>
+ <td colspan="2">Larry the Bird</td>
+ <td>@twitter</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div class="col-md-6">
+ <table class="table table-condensed">
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>First Name</th>
+ <th>Last Name</th>
+ <th>Username</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>1</td>
+ <td>Mark</td>
+ <td>Otto</td>
+ <td>@mdo</td>
+ </tr>
+ <tr>
+ <td>2</td>
+ <td>Jacob</td>
+ <td>Thornton</td>
+ <td>@fat</td>
+ </tr>
+ <tr>
+ <td>3</td>
+ <td colspan="2">Larry the Bird</td>
+ <td>@twitter</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</div>
+
+
+<div class="page-header">
+ <h1>Thumbnails</h1>
+</div>
+<img data-src="holder.js/200x200" class="img-thumbnail"
+ alt="A generic square placeholder image with a white border around it, making it resemble a photograph taken with an old instant camera">
+
+
+<div class="page-header">
+ <h1>Labels</h1>
+</div>
+<h1>
+ <span class="label label-default">Default</span>
+ <span class="label label-primary">Primary</span>
+ <span class="label label-success">Success</span>
+ <span class="label label-info">Info</span>
+ <span class="label label-warning">Warning</span>
+ <span class="label label-danger">Danger</span>
+</h1>
+
+<h2>
+ <span class="label label-default">Default</span>
+ <span class="label label-primary">Primary</span>
+ <span class="label label-success">Success</span>
+ <span class="label label-info">Info</span>
+ <span class="label label-warning">Warning</span>
+ <span class="label label-danger">Danger</span>
+</h2>
+
+<h3>
+ <span class="label label-default">Default</span>
+ <span class="label label-primary">Primary</span>
+ <span class="label label-success">Success</span>
+ <span class="label label-info">Info</span>
+ <span class="label label-warning">Warning</span>
+ <span class="label label-danger">Danger</span>
+</h3>
+<h4>
+ <span class="label label-default">Default</span>
+ <span class="label label-primary">Primary</span>
+ <span class="label label-success">Success</span>
+ <span class="label label-info">Info</span>
+ <span class="label label-warning">Warning</span>
+ <span class="label label-danger">Danger</span>
+</h4>
+<h5>
+ <span class="label label-default">Default</span>
+ <span class="label label-primary">Primary</span>
+ <span class="label label-success">Success</span>
+ <span class="label label-info">Info</span>
+ <span class="label label-warning">Warning</span>
+ <span class="label label-danger">Danger</span>
+</h5>
+<h6>
+ <span class="label label-default">Default</span>
+ <span class="label label-primary">Primary</span>
+ <span class="label label-success">Success</span>
+ <span class="label label-info">Info</span>
+ <span class="label label-warning">Warning</span>
+ <span class="label label-danger">Danger</span>
+</h6>
+
+<p>
+ <span class="label label-default">Default</span>
+ <span class="label label-primary">Primary</span>
+ <span class="label label-success">Success</span>
+ <span class="label label-info">Info</span>
+ <span class="label label-warning">Warning</span>
+ <span class="label label-danger">Danger</span>
+</p>
+
+
+<div class="page-header">
+ <h1>Badges</h1>
+</div>
+<p>
+ <a href="#">Inbox <span class="badge">42</span></a>
+</p>
+<ul class="nav nav-pills">
+ <li class="active"><a href="#">Home <span class="badge">42</span></a></li>
+ <li><a href="#">Profile</a></li>
+ <li><a href="#">Messages <span class="badge">3</span></a></li>
+</ul>
+
+
+<div class="page-header">
+ <h1>Dropdown menus</h1>
+</div>
+<div class="dropdown theme-dropdown clearfix">
+ <a id="dropdownMenu1" href="#" role="button" class="sr-only dropdown-toggle" data-toggle="dropdown">Dropdown <span
+ class="caret"></span></a>
+ <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
+ <li class="active" role="presentation"><a role="menuitem" tabindex="-1" href="#">Action</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Another action</a></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Something else here</a></li>
+ <li role="presentation" class="divider"></li>
+ <li role="presentation"><a role="menuitem" tabindex="-1" href="#">Separated link</a></li>
+ </ul>
+</div>
+
+
+<div class="page-header">
+ <h1>Navs</h1>
+</div>
+<ul class="nav nav-tabs" role="tablist">
+ <li class="active"><a href="#">Home</a></li>
+ <li><a href="#">Profile</a></li>
+ <li><a href="#">Messages</a></li>
+</ul>
+<ul class="nav nav-pills">
+ <li class="active"><a href="#">Home</a></li>
+ <li><a href="#">Profile</a></li>
+ <li><a href="#">Messages</a></li>
+</ul>
+
+
+<div class="page-header">
+ <h1>Navbars</h1>
+</div>
+
+<div class="navbar navbar-default">
+ <div class="container">
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a class="navbar-brand" href="#">Project name</a>
+ </div>
+ <div class="navbar-collapse collapse">
+ <ul class="nav navbar-nav">
+ <li class="active"><a href="#">Home</a></li>
+ <li><a href="#about">About</a></li>
+ <li><a href="#contact">Contact</a></li>
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <span class="caret"></span></a>
+ <ul class="dropdown-menu" role="menu">
+ <li><a href="#">Action</a></li>
+ <li><a href="#">Another action</a></li>
+ <li><a href="#">Something else here</a></li>
+ <li class="divider"></li>
+ <li class="dropdown-header">Nav header</li>
+ <li><a href="#">Separated link</a></li>
+ <li><a href="#">One more separated link</a></li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <!--/.nav-collapse -->
+ </div>
+</div>
+
+<div class="navbar navbar-inverse">
+ <div class="container">
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a class="navbar-brand" href="#">Project name</a>
+ </div>
+ <div class="navbar-collapse collapse">
+ <ul class="nav navbar-nav">
+ <li class="active"><a href="#">Home</a></li>
+ <li><a href="#about">About</a></li>
+ <li><a href="#contact">Contact</a></li>
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <span class="caret"></span></a>
+ <ul class="dropdown-menu" role="menu">
+ <li><a href="#">Action</a></li>
+ <li><a href="#">Another action</a></li>
+ <li><a href="#">Something else here</a></li>
+ <li class="divider"></li>
+ <li class="dropdown-header">Nav header</li>
+ <li><a href="#">Separated link</a></li>
+ <li><a href="#">One more separated link</a></li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+ <!--/.nav-collapse -->
+ </div>
+</div>
+
+
+<div class="page-header">
+ <h1>Alerts</h1>
+</div>
+<div class="alert alert-success" role="alert">
+ <strong>Well done!</strong> You successfully read this important alert message.
+</div>
+<div class="alert alert-info" role="alert">
+ <strong>Heads up!</strong> This alert needs your attention, but it's not super important.
+</div>
+<div class="alert alert-warning" role="alert">
+ <strong>Warning!</strong> Best check yo self, you're not looking too good.
+</div>
+<div class="alert alert-danger" role="alert">
+ <strong>Oh snap!</strong> Change a few things up and try submitting again.
+</div>
+
+
+<div class="page-header">
+ <h1>Progress bars</h1>
+</div>
+<div class="progress">
+ <div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100"
+ style="width: 60%;"><span class="sr-only">60% Complete</span></div>
+</div>
+<div class="progress">
+ <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="40" aria-valuemin="0"
+ aria-valuemax="100" style="width: 40%"><span class="sr-only">40% Complete (success)</span></div>
+</div>
+<div class="progress">
+ <div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="20" aria-valuemin="0"
+ aria-valuemax="100" style="width: 20%"><span class="sr-only">20% Complete</span></div>
+</div>
+<div class="progress">
+ <div class="progress-bar progress-bar-warning" role="progressbar" aria-valuenow="60" aria-valuemin="0"
+ aria-valuemax="100" style="width: 60%"><span class="sr-only">60% Complete (warning)</span></div>
+</div>
+<div class="progress">
+ <div class="progress-bar progress-bar-danger" role="progressbar" aria-valuenow="80" aria-valuemin="0"
+ aria-valuemax="100" style="width: 80%"><span class="sr-only">80% Complete (danger)</span></div>
+</div>
+<div class="progress">
+ <div class="progress-bar progress-bar-striped" role="progressbar" aria-valuenow="60" aria-valuemin="0"
+ aria-valuemax="100" style="width: 60%"><span class="sr-only">100% Complete</span></div>
+</div>
+<div class="progress">
+ <div class="progress-bar progress-bar-success" style="width: 35%"><span
+ class="sr-only">35% Complete (success)</span></div>
+ <div class="progress-bar progress-bar-warning" style="width: 20%"><span
+ class="sr-only">20% Complete (warning)</span></div>
+ <div class="progress-bar progress-bar-danger" style="width: 10%"><span class='sr-only'>10% Complete (danger)</span>
+ </div>
+</div>
+
+
+<div class="page-header">
+ <h1>List groups</h1>
+</div>
+<div class="row">
+ <div class="col-sm-4">
+ <ul class="list-group">
+ <li class="list-group-item">Cras justo odio</li>
+ <li class="list-group-item">Dapibus ac facilisis in</li>
+ <li class="list-group-item">Morbi leo risus</li>
+ <li class="list-group-item">Porta ac consectetur ac</li>
+ <li class="list-group-item">Vestibulum at eros</li>
+ </ul>
+ </div>
+ <!-- /.col-sm-4 -->
+ <div class="col-sm-4">
+ <div class="list-group">
+ <a href="#" class="list-group-item active">
+ Cras justo odio
+ </a>
+ <a href="#" class="list-group-item">Dapibus ac facilisis in</a>
+ <a href="#" class="list-group-item">Morbi leo risus</a>
+ <a href="#" class="list-group-item">Porta ac consectetur ac</a>
+ <a href="#" class="list-group-item">Vestibulum at eros</a>
+ </div>
+ </div>
+ <!-- /.col-sm-4 -->
+ <div class="col-sm-4">
+ <div class="list-group">
+ <a href="#" class="list-group-item active">
+ <h4 class="list-group-item-heading">List group item heading</h4>
+
+ <p class="list-group-item-text">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget
+ risus varius blandit.</p>
+ </a>
+ <a href="#" class="list-group-item">
+ <h4 class="list-group-item-heading">List group item heading</h4>
+
+ <p class="list-group-item-text">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget
+ risus varius blandit.</p>
+ </a>
+ <a href="#" class="list-group-item">
+ <h4 class="list-group-item-heading">List group item heading</h4>
+
+ <p class="list-group-item-text">Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget
+ risus varius blandit.</p>
+ </a>
+ </div>
+ </div>
+ <!-- /.col-sm-4 -->
+</div>
+
+
+<div class="page-header">
+ <h1>Panels</h1>
+</div>
+<div class="row">
+ <div class="col-sm-4">
+ <div class="panel panel-default">
+ <div class="panel-heading">
+ <h3 class="panel-title">Panel title</h3>
+ </div>
+ <div class="panel-body">
+ Panel content
+ </div>
+ </div>
+ <div class="panel panel-primary">
+ <div class="panel-heading">
+ <h3 class="panel-title">Panel title</h3>
+ </div>
+ <div class="panel-body">
+ Panel content
+ </div>
+ </div>
+ </div>
+ <!-- /.col-sm-4 -->
+ <div class="col-sm-4">
+ <div class="panel panel-success">
+ <div class="panel-heading">
+ <h3 class="panel-title">Panel title</h3>
+ </div>
+ <div class="panel-body">
+ Panel content
+ </div>
+ </div>
+ <div class="panel panel-info">
+ <div class="panel-heading">
+ <h3 class="panel-title">Panel title</h3>
+ </div>
+ <div class="panel-body">
+ Panel content
+ </div>
+ </div>
+ </div>
+ <!-- /.col-sm-4 -->
+ <div class="col-sm-4">
+ <div class="panel panel-warning">
+ <div class="panel-heading">
+ <h3 class="panel-title">Panel title</h3>
+ </div>
+ <div class="panel-body">
+ Panel content
+ </div>
+ </div>
+ <div class="panel panel-danger">
+ <div class="panel-heading">
+ <h3 class="panel-title">Panel title</h3>
+ </div>
+ <div class="panel-body">
+ Panel content
+ </div>
+ </div>
+ </div>
+ <!-- /.col-sm-4 -->
+</div>
+
+
+<div class="page-header">
+ <h1>Wells</h1>
+</div>
+<div class="well">
+ <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed diam eget risus varius blandit sit amet non
+ magna. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent commodo cursus magna, vel scelerisque
+ nisl consectetur et. Cras mattis consectetur purus sit amet fermentum. Duis mollis, est non commodo luctus, nisi
+ erat porttitor ligula, eget lacinia odio sem nec elit. Aenean lacinia bibendum nulla sed consectetur.</p>
+</div>
+
+
+</div>
+<!-- /container -->
+
+
+<!-- Bootstrap core JavaScript
+================================================== -->
+<!-- Placed at the end of the document so the pages load faster -->
+<script src="vendor/jquery/dist/jquery.min.js"></script>
+<script src="vendor/bootstrap/dist/js/bootstrap.min.js"></script>
+<script src="js/bootstrap-hadoop.min.js"></script>
+</body>
+</html>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/application/EmbeddedSolrServerFactory.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/application/EmbeddedSolrServerFactory.java
new file mode 100644
index 0000000..2a4b338
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/application/EmbeddedSolrServerFactory.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.hadoop.yarn.appcatalog.application;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
+import org.apache.solr.client.solrj.request.CoreAdminRequest;
+import org.apache.solr.core.NodeConfig;
+import org.apache.solr.core.SolrResourceLoader;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Embedded solr server factory class for unit tests.
+ */
+public final class EmbeddedSolrServerFactory {
+
+ private EmbeddedSolrServerFactory() {
+ }
+
+ /**
+ * Cleans the given solrHome directory and creates a new EmbeddedSolrServer.
+ *
+ * @param solrHome
+ * the Solr home directory to use
+ * @param configSetHome
+ * the directory containing config sets
+ * @param coreName
+ * the name of the core, must have a matching directory in configHome
+ *
+ * @return an EmbeddedSolrServer with a core created for the given coreName
+ * @throws IOException
+ */
+ public static SolrClient create(final String solrHome,
+ final String configSetHome, final String coreName)
+ throws IOException, SolrServerException {
+ return create(solrHome, configSetHome, coreName, true);
+ }
+
+ /**
+ * @param solrHome
+ * the Solr home directory to use
+ * @param configSetHome
+ * the directory containing config sets
+ * @param coreName
+ * the name of the core, must have a matching directory in configHome
+ * @param cleanSolrHome
+ * if true the directory for solrHome will be deleted and re-created
+ * if it already exists
+ *
+ * @return an EmbeddedSolrServer with a core created for the given coreName
+ * @throws IOException
+ */
+ public static SolrClient create(final String solrHome,
+ final String configSetHome, final String coreName,
+ final boolean cleanSolrHome) throws IOException, SolrServerException {
+
+ final File solrHomeDir = new File(solrHome);
+ if (solrHomeDir.exists()) {
+ FileUtils.deleteDirectory(solrHomeDir);
+ solrHomeDir.mkdirs();
+ } else {
+ solrHomeDir.mkdirs();
+ }
+
+ final SolrResourceLoader loader = new SolrResourceLoader(
+ solrHomeDir.toPath());
+ final Path configSetPath = Paths.get(configSetHome).toAbsolutePath();
+
+ final NodeConfig config = new NodeConfig.NodeConfigBuilder(
+ "embeddedSolrServerNode", loader)
+ .setConfigSetBaseDirectory(configSetPath.toString()).build();
+
+ final EmbeddedSolrServer embeddedSolrServer = new EmbeddedSolrServer(config,
+ coreName);
+
+ final CoreAdminRequest.Create createRequest = new CoreAdminRequest.Create();
+ createRequest.setCoreName(coreName);
+ createRequest.setConfigSet(coreName);
+ embeddedSolrServer.request(createRequest);
+
+ return embeddedSolrServer;
+ }
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/application/TestAppCatalogSolrClient.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/application/TestAppCatalogSolrClient.java
new file mode 100644
index 0000000..a43e0d4
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/application/TestAppCatalogSolrClient.java
@@ -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.
+ */
+
+package org.apache.hadoop.yarn.appcatalog.application;
+
+import org.apache.hadoop.yarn.appcatalog.model.AppStoreEntry;
+import org.apache.hadoop.yarn.appcatalog.model.Application;
+import org.apache.solr.client.solrj.SolrClient;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.powermock.api.mockito.PowerMockito;
+import static org.powermock.api.mockito.PowerMockito.when;
+import static org.powermock.api.support.membermodification.MemberMatcher.method;
+
+import static org.junit.Assert.*;
+
+import java.util.List;
+
+/**
+ * Unit test for AppCatalogSolrClient.
+ */
+public class TestAppCatalogSolrClient {
+
+ static final String CONFIGSET_DIR = "src/test/resources/configsets";
+ private static SolrClient solrClient;
+ private static AppCatalogSolrClient spy;
+
+ @Before
+ public void setup() throws Exception {
+ String targetLocation = EmbeddedSolrServerFactory.class
+ .getProtectionDomain().getCodeSource().getLocation().getFile() + "/..";
+
+ String solrHome = targetLocation + "/solr";
+ solrClient = EmbeddedSolrServerFactory.create(solrHome, CONFIGSET_DIR,
+ "exampleCollection");
+ spy = PowerMockito.spy(new AppCatalogSolrClient());
+ when(spy, method(AppCatalogSolrClient.class, "getSolrClient"))
+ .withNoArguments().thenReturn(solrClient);
+ }
+
+ @After
+ public void teardown() throws Exception {
+ try {
+ solrClient.close();
+ } catch (Exception e) {
+ }
+ }
+
+ @Test
+ public void testRegister() throws Exception {
+ Application example = new Application();
+ example.setOrganization("jenkins-ci.org");
+ example.setName("jenkins");
+ example.setDescription("World leading open source automation system.");
+ example.setIcon("/css/img/feather.png");
+ spy.register(example);
+ List<AppStoreEntry> apps = spy.getRecommendedApps();
+ assertEquals(1, apps.size());
+ }
+
+ @Test
+ public void testSearch() throws Exception {
+ Application example = new Application();
+ example.setOrganization("jenkins-ci.org");
+ example.setName("jenkins");
+ example.setDescription("World leading open source automation system.");
+ example.setIcon("/css/img/feather.png");
+ spy.register(example);
+ List<AppStoreEntry> results = spy.search("name_s:jenkins");
+ int expected = 1;
+ int actual = results.size();
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testNotFoundSearch() throws Exception {
+ Application example = new Application();
+ example.setOrganization("jenkins-ci.org");
+ example.setName("jenkins");
+ example.setDescription("World leading open source automation system.");
+ example.setIcon("/css/img/feather.png");
+ spy.register(example);
+ List<AppStoreEntry> results = spy.search("name_s:abc");
+ int expected = 0;
+ int actual = results.size();
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testGetRecommendedApps() throws Exception {
+ List<AppStoreEntry> expected = spy.getRecommendedApps();
+ List<AppStoreEntry> actual = spy.getRecommendedApps();
+ assertEquals(expected, actual);
+ }
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/controller/AppDetailsControllerTest.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/controller/AppDetailsControllerTest.java
new file mode 100644
index 0000000..ca4fba9
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/controller/AppDetailsControllerTest.java
@@ -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.
+ */
+
+package org.apache.hadoop.yarn.appcatalog.controller;
+
+import org.apache.hadoop.yarn.service.api.records.Service;
+import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
+import org.apache.hadoop.yarn.service.api.records.Component;
+import org.apache.hadoop.yarn.service.api.records.Container;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit test for AppDetailsController.
+ */
+public class AppDetailsControllerTest {
+
+ private AppDetailsController controller;
+
+ @Before
+ public void setUp() throws Exception {
+ this.controller = new AppDetailsController();
+
+ }
+
+ @Test
+ public void testGetDetails() throws Exception {
+ String id = "application 1";
+ AppDetailsController ac = Mockito.mock(AppDetailsController.class);
+
+ AppEntry actual = new AppEntry();
+ actual.setName(id);
+ when(ac.getDetails(id)).thenReturn(actual);
+ final AppEntry result = ac.getDetails(id);
+ assertEquals(result, actual);
+ }
+
+ @Test
+ public void testGetStatus() throws Exception {
+ String id = "application 1";
+ AppDetailsController ac = Mockito.mock(AppDetailsController.class);
+
+ Service yarnfile = new Service();
+ Component comp = new Component();
+ Container c = new Container();
+ c.setId("container-1");
+ List<Container> containers = new ArrayList<Container>();
+ containers.add(c);
+ comp.setContainers(containers);
+ yarnfile.addComponent(comp);
+ AppEntry actual = new AppEntry();
+ actual.setName(id);
+ actual.setYarnfile(yarnfile);
+ when(ac.getStatus(id)).thenReturn(actual);
+ final AppEntry result = ac.getStatus(id);
+ assertEquals(result, actual);
+ }
+
+ @Test
+ public void testStopApp() throws Exception {
+ String id = "application 1";
+ AppDetailsController ac = Mockito.mock(AppDetailsController.class);
+
+ Service yarnfile = new Service();
+ Component comp = new Component();
+ Container c = new Container();
+ c.setId("container-1");
+ List<Container> containers = new ArrayList<Container>();
+ containers.add(c);
+ comp.setContainers(containers);
+ yarnfile.addComponent(comp);
+ Response expected = Response.ok().build();
+ when(ac.stopApp(id)).thenReturn(Response.ok().build());
+ final Response actual = ac.stopApp(id);
+ assertEquals(expected.getStatus(), actual.getStatus());
+ }
+
+ @Test
+ public void testRestartApp() throws Exception {
+ String id = "application 1";
+ AppDetailsController ac = Mockito.mock(AppDetailsController.class);
+
+ Service yarnfile = new Service();
+ Component comp = new Component();
+ Container c = new Container();
+ c.setId("container-1");
+ List<Container> containers = new ArrayList<Container>();
+ containers.add(c);
+ comp.setContainers(containers);
+ yarnfile.addComponent(comp);
+ Response expected = Response.ok().build();
+ when(ac.restartApp(id)).thenReturn(Response.ok().build());
+ final Response actual = ac.restartApp(id);
+ assertEquals(expected.getStatus(), actual.getStatus());
+ }
+
+ @Test
+ public void testPathAnnotation() throws Exception {
+ assertNotNull(this.controller.getClass()
+ .getAnnotations());
+ assertThat("The controller has the annotation Path",
+ this.controller.getClass()
+ .isAnnotationPresent(Path.class));
+
+ final Path path = this.controller.getClass()
+ .getAnnotation(Path.class);
+ assertThat("The path is /app_details", path.value(),
+ is("/app_details"));
+ }
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/controller/AppListControllerTest.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/controller/AppListControllerTest.java
new file mode 100644
index 0000000..97f288e
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/controller/AppListControllerTest.java
@@ -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.
+ */
+
+package org.apache.hadoop.yarn.appcatalog.controller;
+
+import org.apache.hadoop.yarn.appcatalog.model.AppEntry;
+import org.apache.hadoop.yarn.service.api.records.Service;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit test for AppListController.
+ */
+public class AppListControllerTest {
+
+ private AppListController controller;
+
+ @Before
+ public void setUp() throws Exception {
+ this.controller = new AppListController();
+
+ }
+
+ @Test
+ public void testGetList() throws Exception {
+ AppListController ac = Mockito.mock(AppListController.class);
+
+ List<AppEntry> actual = new ArrayList<AppEntry>();
+ when(ac.getList()).thenReturn(actual);
+ final List<AppEntry> result = ac.getList();
+ assertEquals(result, actual);
+ }
+
+ @Test
+ public void testDelete() throws Exception {
+ String id = "application 1";
+ AppListController ac = Mockito.mock(AppListController.class);
+
+ Response expected = Response.ok().build();
+ when(ac.delete(id, id)).thenReturn(Response.ok().build());
+ final Response actual = ac.delete(id, id);
+ assertEquals(expected.getStatus(), actual.getStatus());
+ }
+
+ @Test
+ public void testDeploy() throws Exception {
+ String id = "application 1";
+ AppListController ac = Mockito.mock(AppListController.class);
+ Service service = new Service();
+ Response expected = Response.ok().build();
+ when(ac.deploy(id, service)).thenReturn(Response.ok().build());
+ final Response actual = ac.deploy(id, service);
+ assertEquals(expected.getStatus(), actual.getStatus());
+ }
+
+ @Test
+ public void testPathAnnotation() throws Exception {
+ assertNotNull(this.controller.getClass()
+ .getAnnotations());
+ assertThat("The controller has the annotation Path",
+ this.controller.getClass().isAnnotationPresent(Path.class));
+
+ final Path path = this.controller.getClass()
+ .getAnnotation(Path.class);
+ assertThat("The path is /app_list", path.value(), is("/app_list"));
+ }
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/controller/AppStoreControllerTest.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/controller/AppStoreControllerTest.java
new file mode 100644
index 0000000..d09952b
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/java/org/apache/hadoop/yarn/appcatalog/controller/AppStoreControllerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.hadoop.yarn.appcatalog.controller;
+
+import org.apache.hadoop.yarn.appcatalog.model.AppStoreEntry;
+import org.apache.hadoop.yarn.appcatalog.model.Application;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Response;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for AppStoreController.
+ */
+public class AppStoreControllerTest {
+
+ private AppStoreController controller;
+
+ @Before
+ public void setUp() throws Exception {
+ this.controller = new AppStoreController();
+
+ }
+
+ @Test
+ public void testGetRecommended() throws Exception {
+ AppStoreController ac = Mockito.mock(AppStoreController.class);
+ List<AppStoreEntry> actual = new ArrayList<AppStoreEntry>();
+ when(ac.get()).thenReturn(actual);
+ final List<AppStoreEntry> result = ac.get();
+ assertEquals(result, actual);
+ }
+
+ @Test
+ public void testSearch() throws Exception {
+ String keyword = "jenkins";
+ AppStoreController ac = Mockito.mock(AppStoreController.class);
+ List<AppStoreEntry> expected = new ArrayList<AppStoreEntry>();
+ when(ac.search(keyword)).thenReturn(expected);
+ final List<AppStoreEntry> actual = ac.search(keyword);
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testRegister() throws Exception {
+ AppStoreController ac = Mockito.mock(AppStoreController.class);
+ Application app = new Application();
+ app.setName("jenkins");
+ app.setOrganization("jenkins.org");
+ app.setDescription("This is a description");
+ app.setIcon("/css/img/feather.png");
+ Response expected = Response.ok().build();
+ when(ac.register(app)).thenReturn(Response.ok().build());
+ final Response actual = ac.register(app);
+ assertEquals(expected.getStatus(), actual.getStatus());
+ }
+
+ @Test
+ public void testPathAnnotation() throws Exception {
+ assertNotNull(this.controller.getClass()
+ .getAnnotations());
+ assertThat("The controller has the annotation Path",
+ this.controller.getClass()
+ .isAnnotationPresent(Path.class));
+
+ final Path path = this.controller.getClass()
+ .getAnnotation(Path.class);
+ assertThat("The path is /app_store", path.value(), is("/app_store"));
+ }
+
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/javascript/controllersSpec.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/javascript/controllersSpec.js
new file mode 100644
index 0000000..ec58934
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/javascript/controllersSpec.js
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+describe('Controller tests', function () {
+
+ // Unit test for listing, and start/stop applications.
+ describe('AppListController', function() {
+ var scope, ctrl, http, httpBackend;
+
+ beforeEach(module('app'));
+ beforeEach(inject(function ($controller, $rootScope, $http, $httpBackend) {
+ scope = $rootScope.$new();
+ rootScope = $rootScope;
+ http = $http;
+ httpBackend = $httpBackend;
+ ctrl = $controller('AppListController', {$scope: scope});
+ }));
+
+ afterEach(function() {
+ httpBackend.verifyNoOutstandingExpectation();
+ httpBackend.verifyNoOutstandingRequest();
+ });
+
+ it('should contain appList', function () {
+ httpBackend.expectGET('/v1/app_list').respond(200, [{id:"jenkins",name:"jenkins",app:"",yarnfile:{}}]);
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ httpBackend.flush();
+ expect(scope.appList.length).toBe(1);
+ });
+
+ it('should test to delete app', function () {
+ httpBackend.expectGET('/v1/app_list').respond(200, [{id:"jenkins",name:"jenkins",app:"",yarnfile:{}}]);
+ httpBackend.expectDELETE('/v1/app_list/jenkins/jenkins').respond(200, {data:"Application Deleted."});
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ spyOn(rootScope, '$emit');
+ scope.$apply(function() {
+ scope.deleteApp("jenkins","jenkins");
+ });
+ httpBackend.flush();
+ expect(rootScope.$emit).toHaveBeenCalledWith('RefreshAppList', {});
+ });
+
+ it('should test to refresh appList', function() {
+ spyOn(rootScope, '$emit');
+ httpBackend.expectGET('/v1/app_list').respond(200, [{id:"jenkins",name:"jenkins",app:"",yarnfile:{}}]);
+ httpBackend.expectGET('/v1/app_list').respond(200, [{id:"jenkins",name:"jenkins",app:"",yarnfile:{}}]);
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ scope.$apply(function() {
+ scope.refreshList();
+ });
+ httpBackend.flush();
+ expect(rootScope.$emit).toHaveBeenCalledWith('hideLoadScreen', {});
+ })
+ });
+
+ // Unit test for inspect YARN application details.
+ describe('AppDetailsController', function() {
+ var scope, ctrl, http, routeParams, httpBackend;
+
+ beforeEach(module('app'));
+ beforeEach(inject(function ($controller, $rootScope, $http, $routeParams, $httpBackend) {
+ scope = $rootScope.$new();
+ rootScope = $rootScope;
+ http = $http;
+ routeParams = $routeParams;
+ httpBackend = $httpBackend;
+ ctrl = $controller('AppDetailsController', {$scope: scope});
+ }));
+
+ afterEach(function() {
+ httpBackend.verifyNoOutstandingExpectation();
+ httpBackend.verifyNoOutstandingRequest();
+ });
+
+ it('should contain unknown state', function () {
+ httpBackend.expectGET('/v1/app_details/config/undefined').respond(200, {"yarnfile":{"state":"UNKNOWN","components":[]}});
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ httpBackend.flush();
+ expect(scope.details.yarnfile.state).toBe("UNKNOWN");
+ });
+
+ it('should run test to refrshed details', function () {
+ httpBackend.expectGET('/v1/app_details/config/undefined').respond(200, {"yarnfile":{"state":"UNKNOWN","components":[]}});
+ httpBackend.expectGET('/v1/app_details/status/aabbccdd').respond(200, {yarnfile:{state: "ACCEPTED", components:[]}});
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ scope.$apply(function() {
+ routeParams.id = "aabbccdd";
+ scope.appName = "aabbccdd";
+ scope.refreshAppDetails();
+ });
+ httpBackend.flush();
+ expect(scope.details.yarnfile.state).toBe("ACCEPTED");
+ });
+
+ it('should run test to restart app', function () {
+ httpBackend.expectGET('/v1/app_details/config/undefined').respond(200, {"yarnfile":{"state":"UNKNOWN","components":[]}});
+ httpBackend.expectPOST('/v1/app_details/restart/aabbccdd').respond(200, {yarnfile:{state: "ACCEPTED", components:[]}});
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ httpBackend.expectGET('/v1/app_details/status/undefined').respond(200, {yarnfile:{state: "ACCEPTED", components:[]}});
+ scope.$apply(function() {
+ scope.restartApp("aabbccdd");
+ });
+ httpBackend.flush();
+ expect(scope.details.yarnfile.components).toBe();
+ });
+
+ it('should run test to stop app', function () {
+ httpBackend.expectGET('/v1/app_details/config/undefined').respond(200, {"yarnfile":{"state":"UNKNOWN","components":[]}});
+ httpBackend.expectPOST('/v1/app_details/stop/aabbccdd').respond(200, {yarnfile:{state: "STOPPED", components:[]}});
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ httpBackend.expectGET('/v1/app_details/status/undefined').respond(200, {yarnfile:{state: "ACCEPTED", components:[]}});
+ scope.$apply(function() {
+ scope.stopApp("aabbccdd");
+ });
+ httpBackend.flush();
+ expect(scope.details.yarnfile.components).toBe();
+ });
+
+ });
+
+ // Unit test for deploying app, and search for apps from Yarn Appstore.
+ describe('AppStoreController', function() {
+ var scope, ctrl, http, httpBackend;
+
+ beforeEach(module('app'));
+ beforeEach(inject(function ($controller, $rootScope, $http, $httpBackend) {
+ scope = $rootScope.$new();
+ http = $http;
+ httpBackend = $httpBackend;
+ ctrl = $controller('AppStoreController', {$scope: scope});
+ }));
+
+ afterEach(function() {
+ httpBackend.verifyNoOutstandingExpectation();
+ httpBackend.verifyNoOutstandingRequest();
+ });
+
+ it('should contain appStore', function () {
+ httpBackend.expectGET('/v1/app_store/recommended').respond(200, "");
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ httpBackend.flush();
+ expect(scope.appStore.length).toBe(0);
+ });
+
+ it('should run test to deploy app', function() {
+ httpBackend.expectGET('/v1/app_store/recommended').respond(200, "");
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ httpBackend.flush();
+ scope.$apply(function() {
+ scope.deployApp("aabbccdd");
+ });
+ expect(scope.appStore.length).toBe(0);
+ });
+
+ it('should run test to search for apps', function() {
+ httpBackend.expectGET('/v1/app_store/recommended').respond(200, "");
+ httpBackend.expectGET('/v1/app_store/search?q=aabbccdd').respond(204, {data:'ACCEPTED'});
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ scope.$apply(function() {
+ scope.searchText = "aabbccdd";
+ scope.change("aabbccdd");
+ });
+ httpBackend.flush();
+ expect(scope.appStore.data).toBe('ACCEPTED');
+ });
+
+ });
+
+ // Unit test cases for creating a new YARN application.
+ describe('NewAppController', function() {
+ var scope, ctrl, http, httpBackend;
+
+ beforeEach(module('app'));
+ beforeEach(inject(function ($controller, $rootScope, $http, $httpBackend) {
+ scope = $rootScope.$new();
+ http = $http;
+ httpBackend = $httpBackend;
+ ctrl = $controller('NewAppController', {$scope: scope});
+ }));
+
+ afterEach(function() {
+ httpBackend.verifyNoOutstandingExpectation();
+ httpBackend.verifyNoOutstandingRequest();
+ });
+
+ it('should contain details', function () {
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ httpBackend.flush();
+ expect(scope.details.name).toBe("");
+ });
+
+ it('should run test to register data to backend', function() {
+ httpBackend.expectPOST('/v1/app_store/register').respond(204, {data:'ACCEPTED'});
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ scope.$apply(function() {
+ scope.save();
+ });
+ httpBackend.flush();
+ expect(scope.message).toEqual("Application published successfully.");
+ });
+
+ it('should run test to fail register data to backend', function() {
+ httpBackend.expectPOST('/v1/app_store/register').respond(500, {data:'INTERNAL SERVER ERROR'});
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ scope.$apply(function() {
+ scope.save();
+ });
+ httpBackend.flush();
+ expect(scope.error).toEqual("Error in registering application configuration.");
+ });
+
+ it('should run test to add more component to details', function() {
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ expect(scope.details.components.length).toEqual(1);
+ scope.$apply(function() {
+ scope.add();
+ });
+ httpBackend.flush();
+ expect(scope.details.components.length).toEqual(2);
+ });
+
+ it('should run test to remove second component', function() {
+ httpBackend.expectGET('partials/home.html').respond(200, "");
+ expect(scope.details.components.length).toEqual(1);
+ scope.$apply(function() {
+ scope.add();
+ scope.remove(1);
+ });
+ httpBackend.flush();
+ expect(scope.details.components.length).toEqual(1);
+ });
+ });
+
+});
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/javascript/karma.conf.js b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/javascript/karma.conf.js
new file mode 100644
index 0000000..f7620a4
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/javascript/karma.conf.js
@@ -0,0 +1,34 @@
+module.exports = function(config){
+ config.set({
+
+ basePath : '../../../',
+
+ files : [
+ 'target/generated-sources/vendor/angular**/**.min.js',
+ 'target/generated-sources/vendor/angular-mocks/angular-mocks.js',
+ 'src/main/javascript/**/*.js',
+ 'src/test/javascript/**/*Spec.js',
+ 'src/test/javascript/**/!(karma.conf).js'
+ ],
+
+ autoWatch : true,
+
+ frameworks: ['jasmine'],
+
+ browsers: ['PhantomJS'],
+
+ plugins : [
+ 'karma-chrome-launcher',
+ 'karma-firefox-launcher',
+ 'karma-phantomjs-launcher',
+ 'karma-jasmine',
+ 'karma-junit-reporter'
+ ],
+
+ junitReporter : {
+ outputFile: 'target/test_out/unit.xml',
+ suite: 'src/test/javascript'
+ }
+
+ });
+};
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets.tgz b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets.tgz
new file mode 100644
index 0000000..3352d31
Binary files /dev/null and b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets.tgz differ
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/lang/stopwords_en.txt b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/lang/stopwords_en.txt
new file mode 100644
index 0000000..2c164c0
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/lang/stopwords_en.txt
@@ -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.
+
+# a couple of test stopwords to test that the words are really being
+# configured from this file:
+stopworda
+stopwordb
+
+# Standard english stop words taken from Lucene's StopAnalyzer
+a
+an
+and
+are
+as
+at
+be
+but
+by
+for
+if
+in
+into
+is
+it
+no
+not
+of
+on
+or
+such
+that
+the
+their
+then
+there
+these
+they
+this
+to
+was
+will
+with
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/params.json b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/params.json
new file mode 100644
index 0000000..06114ef
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/params.json
@@ -0,0 +1,20 @@
+{"params":{
+ "query":{
+ "defType":"edismax",
+ "q.alt":"*:*",
+ "rows":"10",
+ "fl":"*,score",
+ "":{"v":0}
+ },
+ "facets":{
+ "facet":"on",
+ "facet.mincount": "1",
+ "":{"v":0}
+ },
+ "velocity":{
+ "wt": "velocity",
+ "v.template":"browse",
+ "v.layout": "layout",
+ "":{"v":0}
+ }
+}}
\ No newline at end of file
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/protwords.txt b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/protwords.txt
new file mode 100644
index 0000000..4341c05
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/protwords.txt
@@ -0,0 +1,20 @@
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#-----------------------------------------------------------------------
+# Use a protected word file to protect against the stemmer reducing two
+# unrelated words to the same base word.
+
+# Some non-words that normally won't be encountered,
+# just to test that they won't be stemmed.
+dontstems
+zwhacky
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/schema.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/schema.xml
new file mode 100644
index 0000000..20acbc9
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/schema.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<schema name="exampleCollection" version="1.6">
+
+ <!-- Defined fields -->
+ <field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" docValues="true" />
+ <field name="type_s" type="string" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
+ <field name="org_s" type="string" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
+ <field name="name_s" type="string" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
+ <field name="desc_s" type="string" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
+ <field name="icon_s" type="string" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
+ <field name="yarnfile_s" type="string" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
+ <field name="like_i" type="int" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
+ <field name="download_i" type="int" indexed="true" stored="true" required="false" multiValued="false" docValues="true" />
+ <field name="_version_" type="long" indexed="true" stored="false"/>
+
+ <uniqueKey>id</uniqueKey>
+
+ <!-- Field types -->
+ <fieldType name="string" class="solr.StrField" sortMissingLast="true" docValues="true" />
+ <fieldType name="strings" class="solr.StrField" sortMissingLast="true" multiValued="true" docValues="true" />
+ <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>
+ <fieldType name="booleans" class="solr.BoolField" sortMissingLast="true" multiValued="true"/>
+ <fieldType name="int" class="solr.TrieIntField" docValues="true" precisionStep="0" positionIncrementGap="0"/>
+ <fieldType name="float" class="solr.TrieFloatField" docValues="true" precisionStep="0" positionIncrementGap="0"/>
+ <fieldType name="long" class="solr.TrieLongField" docValues="true" precisionStep="0" positionIncrementGap="0"/>
+ <fieldType name="double" class="solr.TrieDoubleField" docValues="true" precisionStep="0" positionIncrementGap="0"/>
+ <fieldType name="ints" class="solr.TrieIntField" docValues="true" precisionStep="0" positionIncrementGap="0" multiValued="true"/>
+ <fieldType name="floats" class="solr.TrieFloatField" docValues="true" precisionStep="0" positionIncrementGap="0" multiValued="true"/>
+ <fieldType name="longs" class="solr.TrieLongField" docValues="true" precisionStep="0" positionIncrementGap="0" multiValued="true"/>
+ <fieldType name="doubles" class="solr.TrieDoubleField" docValues="true" precisionStep="0" positionIncrementGap="0" multiValued="true"/>
+ <fieldType name="tint" class="solr.TrieIntField" docValues="true" precisionStep="8" positionIncrementGap="0"/>
+ <fieldType name="tfloat" class="solr.TrieFloatField" docValues="true" precisionStep="8" positionIncrementGap="0"/>
+ <fieldType name="tlong" class="solr.TrieLongField" docValues="true" precisionStep="8" positionIncrementGap="0"/>
+ <fieldType name="tdouble" class="solr.TrieDoubleField" docValues="true" precisionStep="8" positionIncrementGap="0"/>
+ <fieldType name="tints" class="solr.TrieIntField" docValues="true" precisionStep="8" positionIncrementGap="0" multiValued="true"/>
+ <fieldType name="tfloats" class="solr.TrieFloatField" docValues="true" precisionStep="8" positionIncrementGap="0" multiValued="true"/>
+ <fieldType name="tlongs" class="solr.TrieLongField" docValues="true" precisionStep="8" positionIncrementGap="0" multiValued="true"/>
+ <fieldType name="tdoubles" class="solr.TrieDoubleField" docValues="true" precisionStep="8" positionIncrementGap="0" multiValued="true"/>
+ <fieldType name="date" class="solr.TrieDateField" docValues="true" precisionStep="0" positionIncrementGap="0"/>
+ <fieldType name="dates" class="solr.TrieDateField" docValues="true" precisionStep="0" positionIncrementGap="0" multiValued="true"/>
+ <fieldType name="tdate" class="solr.TrieDateField" docValues="true" precisionStep="6" positionIncrementGap="0"/>
+ <fieldType name="tdates" class="solr.TrieDateField" docValues="true" precisionStep="6" positionIncrementGap="0" multiValued="true"/>
+ <fieldType name="binary" class="solr.BinaryField"/>
+ <fieldType name="random" class="solr.RandomSortField" indexed="true" />
+
+ <!-- A text field with defaults appropriate for English: it
+ tokenizes with StandardTokenizer, removes English stop words
+ (lang/stopwords_en.txt), down cases, protects words from protwords.txt, and
+ finally applies Porter's stemming. The query time analyzer
+ also applies synonyms from synonyms.txt. -->
+ <fieldType name="text_en" class="solr.TextField" positionIncrementGap="100">
+ <analyzer type="index">
+ <tokenizer class="solr.StandardTokenizerFactory"/>
+ <!-- in this example, we will only use synonyms at query time
+ <filter class="solr.SynonymFilterFactory" synonyms="index_synonyms.txt" ignoreCase="true" expand="false"/> -->
+ <!-- Case insensitive stop word removal. -->
+ <filter class="solr.StopFilterFactory"
+ ignoreCase="true"
+ words="lang/stopwords_en.txt"
+ />
+ <filter class="solr.LowerCaseFilterFactory"/>
+ <filter class="solr.EnglishPossessiveFilterFactory"/>
+ <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+ <!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
+ <filter class="solr.EnglishMinimalStemFilterFactory"/>
+ -->
+ <filter class="solr.PorterStemFilterFactory"/>
+ </analyzer>
+ <analyzer type="query">
+ <tokenizer class="solr.StandardTokenizerFactory"/>
+ <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
+ <filter class="solr.StopFilterFactory"
+ ignoreCase="true"
+ words="lang/stopwords_en.txt"
+ />
+ <filter class="solr.LowerCaseFilterFactory"/>
+ <filter class="solr.EnglishPossessiveFilterFactory"/>
+ <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
+ <!-- Optionally you may want to use this less aggressive stemmer instead of PorterStemFilterFactory:
+ <filter class="solr.EnglishMinimalStemFilterFactory"/>
+ -->
+ <filter class="solr.PorterStemFilterFactory"/>
+ </analyzer>
+ </fieldType>
+
+ <!-- lowercases the entire field value, keeping it as a single token. -->
+ <fieldType name="lowercase" class="solr.TextField" positionIncrementGap="100">
+ <analyzer>
+ <tokenizer class="solr.KeywordTokenizerFactory"/>
+ <filter class="solr.LowerCaseFilterFactory" />
+ </analyzer>
+ </fieldType>
+
+ <!-- since fields of this type are by default not stored or indexed,
+ any data added to them will be ignored outright. -->
+ <fieldType name="ignored" stored="false" indexed="false" docValues="false" multiValued="true" class="solr.StrField" />
+
+</schema>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/solrconfig.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/solrconfig.xml
new file mode 100644
index 0000000..392feec
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/solrconfig.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<config>
+ <luceneMatchVersion>6.2.1</luceneMatchVersion>
+
+ <dataDir>${solr.data.dir:}</dataDir>
+
+ <schemaFactory class="ClassicIndexSchemaFactory"/>
+
+ <indexConfig>
+ <lockType>single</lockType>
+ </indexConfig>
+
+ <requestDispatcher handleSelect="false">
+ <httpCaching never304="true" />
+ </requestDispatcher>
+
+ <requestHandler name="/select" class="solr.SearchHandler" />
+ <requestHandler name="/update" class="solr.UpdateRequestHandler" />
+
+</config>
\ No newline at end of file
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/stopwords.txt b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/stopwords.txt
new file mode 100644
index 0000000..ae1e83e
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/stopwords.txt
@@ -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
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/synonyms.txt b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/synonyms.txt
new file mode 100644
index 0000000..0ef0e8d
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/configsets/exampleCollection/conf/synonyms.txt
@@ -0,0 +1,28 @@
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#-----------------------------------------------------------------------
+#some test synonym mappings unlikely to appear in real input text
+aaafoo => aaabar
+bbbfoo => bbbfoo bbbbar
+cccfoo => cccbar cccbaz
+fooaaa,baraaa,bazaaa
+
+# Some synonym groups specific to this example
+GB,gib,gigabyte,gigabytes
+MB,mib,megabyte,megabytes
+Television, Televisions, TV, TVs
+#notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming
+#after us won't split it into two words.
+
+# Synonym mappings can be used for spelling correction too
+pixima => pixma
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/log4j.properties b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/log4j.properties
new file mode 100644
index 0000000..eb04743
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/hadoop-yarn-applications-catalog-webapp/src/test/resources/log4j.properties
@@ -0,0 +1,11 @@
+log4j.rootLogger = INFO, CATALINA
+
+# Define all the appenders
+log4j.appender.CATALINA = org.apache.log4j.DailyRollingFileAppender
+log4j.appender.CATALINA.File = target/appcatalog.log
+log4j.appender.CATALINA.Append = true
+log4j.appender.CATALINA.Encoding = UTF-8
+# Roll-over the log once per day
+log4j.appender.CATALINA.DatePattern = '.'yyyy-MM-dd'.log'
+log4j.appender.CATALINA.layout = org.apache.log4j.PatternLayout
+log4j.appender.CATALINA.layout.ConversionPattern = %d [%t] %-5p %c- %m%n
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/pom.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/pom.xml
new file mode 100644
index 0000000..770bf24
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-applications-catalog/pom.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>hadoop-yarn-applications</artifactId>
+ <groupId>org.apache.hadoop</groupId>
+ <version>3.3.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-yarn-applications-catalog</artifactId>
+ <packaging>pom</packaging>
+
+ <name>YARN Application Catalog</name>
+
+ <url>http://hadoop.apache.org</url>
+
+ <modules>
+ <module>hadoop-yarn-applications-catalog-webapp</module>
+ <module>hadoop-yarn-applications-catalog-docker</module>
+ </modules>
+</project>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/java/org/apache/hadoop/yarn/service/client/ApiServiceClient.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/java/org/apache/hadoop/yarn/service/client/ApiServiceClient.java
index 94f03c3..834bb03 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/java/org/apache/hadoop/yarn/service/client/ApiServiceClient.java
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-api/src/main/java/org/apache/hadoop/yarn/service/client/ApiServiceClient.java
@@ -77,6 +77,13 @@ public class ApiServiceClient extends AppAdminClient {
private static final Base64 BASE_64_CODEC = new Base64(0);
protected YarnClient yarnClient;
+ public ApiServiceClient() {
+ }
+
+ public ApiServiceClient(Configuration c) throws Exception {
+ serviceInit(c);
+ }
+
@Override protected void serviceInit(Configuration configuration)
throws Exception {
yarnClient = YarnClient.createYarnClient();
@@ -151,7 +158,7 @@ public class ApiServiceClient extends AppAdminClient {
* @return URI to API Service
* @throws IOException
*/
- protected String getServicePath(String appName) throws IOException {
+ public String getServicePath(String appName) throws IOException {
String url = getRMWebAddress();
StringBuilder api = new StringBuilder();
api.append(url)
@@ -215,7 +222,7 @@ public class ApiServiceClient extends AppAdminClient {
}
}
- private Builder getApiClient() throws IOException {
+ public Builder getApiClient() throws IOException {
return getApiClient(getServicePath(null));
}
@@ -226,7 +233,7 @@ public class ApiServiceClient extends AppAdminClient {
* @return
* @throws IOException
*/
- private Builder getApiClient(String requestPath)
+ public Builder getApiClient(String requestPath)
throws IOException {
Client client = Client.create(getClientConfig());
client.setChunkedEncodingSize(null);
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/examples/appcatalog/appcatalog.json b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/examples/appcatalog/appcatalog.json
new file mode 100755
index 0000000..6a5f2f3
--- /dev/null
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/hadoop-yarn-services/hadoop-yarn-services-core/examples/appcatalog/appcatalog.json
@@ -0,0 +1,28 @@
+{
+ "name": "appcatalog",
+ "version": "1",
+ "components" :
+ [
+ {
+ "name": "catalog",
+ "number_of_containers": 1,
+ "artifact": {
+ "id": "apache/hadoop-yarn-applications-catalog-docker:3.3.0-SNAPSHOT",
+ "type": "DOCKER"
+ },
+ "resource": {
+ "cpus": 1,
+ "memory": "2048"
+ },
+ "configuration": {
+ "env": {
+ "YARN_CONTAINER_RUNTIME_DOCKER_RUN_OVERRIDE_DISABLE":"true",
+ "YARN_CONTAINER_RUNTIME_DOCKER_MOUNTS":"/etc/hadoop/conf:/etc/hadoop/conf:ro,/var/lib/sss/pipes:/var/lib/sss/pipes:rw",
+ "JAVA_HOME":"/usr/lib/jvm/jre-1.8.0"
+ },
+ "properties": {
+ }
+ }
+ }
+ ]
+}
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/pom.xml b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/pom.xml
index 61ca77a..78b709a 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/pom.xml
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-applications/pom.xml
@@ -37,6 +37,7 @@
<module>hadoop-yarn-applications-distributedshell</module>
<module>hadoop-yarn-applications-unmanaged-am-launcher</module>
<module>hadoop-yarn-services</module>
+ <module>hadoop-yarn-applications-catalog</module>
</modules>
<profiles>
diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/Examples.md b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/Examples.md
index da7a9c4..e0d1c01 100644
--- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/Examples.md
+++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/markdown/yarn-service/Examples.md
@@ -165,6 +165,19 @@ where `service-name` is optional. If omitted, it uses the name defined in the `Y
Look up your IPs at the RM REST endpoint `http://<RM host>:8088/app/v1/services/httpd-service`.
Then visit port 8080 for each IP to view the pages.
+## Application Catalog - appcatalog
+
+Application Catalog introduces many exciting new features for deploying Hadoop software that benefit both administrators and end users. With Application Catalog, user gets a personalized view of the software status in Hadoop. In addition, users can install or register applications by using web-based user interface.
+
+To start Application Catalog service with the command:
+```
+yarn app -launch <service-name> appcatalog
+```
+where `service-name` is user defined name.
+
+The deployment progress of the application catalog is located in Resource Manager UI. When the service reaches STABLE state, application catalog UI is available at:
+http://appcatalog.${SERVICE_NAME}.${USER}.${DOMAIN}:8080/
+
## Docker image ENTRYPOINT support
Docker images may have built with ENTRYPOINT to enable start up of docker image without any parameters.
---------------------------------------------------------------------
To unsubscribe, e-mail: common-commits-unsubscribe@hadoop.apache.org
For additional commands, e-mail: common-commits-help@hadoop.apache.org