├── .gitignore ├── Global.scala ├── README.md ├── app ├── controllers │ ├── Application.scala │ ├── apps │ │ ├── SparkList.scala │ │ └── YarnList.scala │ ├── auth │ │ ├── Authentication.scala │ │ ├── Secured.scala │ │ └── userName.scala │ └── spark │ │ ├── SparkJar.scala │ │ └── TaskManager.scala ├── models │ ├── Mail │ │ ├── Email.scala │ │ └── RegisterExecption.scala │ ├── actor │ │ ├── ActorStack.scala │ │ ├── InstrumentedActor.scala │ │ ├── Slf4jLogging.scala │ │ └── WebSocketChannel.scala │ ├── depend │ │ └── Depend.scala │ ├── deploy │ │ ├── CommonMessages.scala │ │ ├── CreateBatchRequest.scala │ │ ├── Execute.scala │ │ ├── JobManager.scala │ │ ├── MatchEngine.scala │ │ ├── MessagePool.scala │ │ ├── StatusListener.scala │ │ ├── package.scala │ │ ├── process │ │ │ ├── LineBufferedProcess.scala │ │ │ ├── LineBufferedStream.scala │ │ │ └── SparkProcessBuilder.scala │ │ └── task │ │ │ ├── TaskDataProvider.scala │ │ │ └── TaskProvider.scala │ ├── io │ │ ├── JobDAO.scala │ │ ├── JobFileDAO.scala │ │ ├── Message.scala │ │ ├── MetricsData.scala │ │ ├── TaskDao.scala │ │ ├── TaskInfoDao.scala │ │ └── package.scala │ ├── metrics │ │ ├── BaseInfo.scala │ │ ├── HadoopMetricsProvider.scala │ │ ├── MetricsFactory.scala │ │ └── MetricsProvider.scala │ ├── shell │ │ └── kill_job.sh │ ├── user │ │ ├── User.scala │ │ └── package.scala │ └── utils │ │ ├── Captcha.scala │ │ ├── Config.scala │ │ ├── Configuration.scala │ │ ├── JsonParse.scala │ │ └── MD5Utils.scala └── views │ ├── copyright.scala.html │ ├── error.scala.html │ ├── errors │ └── fzf.scala.html │ ├── findpwd.scala.html │ ├── index.scala.html │ ├── login.scala.html │ ├── main.scala.html │ ├── nothing.scala.html │ ├── plain.scala.html │ ├── registed.scala.html │ ├── register.scala.html │ ├── setpwd.scala.html │ ├── sparklist.scala.html │ ├── tasklist.scala.html │ ├── upload.scala.html │ └── yarnlist.scala.html ├── build.sbt ├── conf ├── application.conf ├── evolutions │ └── default │ │ ├── 1.sql │ │ ├── 2.sql │ │ ├── 3.sql │ │ ├── 4.sql │ │ ├── 5.sql │ │ └── 6.sql ├── routes └── web-site.conf ├── db ├── play.h2.db ├── play.h2.db.trace.db └── play.trace.db ├── project ├── .sbtserver ├── .sbtserver.lock ├── build.properties ├── plugins.sbt └── sbt-ui.sbt ├── public ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── fontawesome-webfont.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ ├── glyphicons-halflings-regular.woff2 │ ├── linecons.eot │ ├── linecons.svg │ ├── linecons.ttf │ └── linecons.woff ├── images │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 522641-46907f6c8f3b2b0b.png │ ├── QQ20170313-162151@2x.png │ ├── QQ20170313-171517@2x.png │ ├── album-image-full.jpg │ ├── album-img-1.png │ ├── album-img-2.png │ ├── album-img-3.png │ ├── album-img-4.png │ ├── album-img-5.png │ ├── album-img-6.png │ ├── album-img-7.png │ ├── album-img-8.png │ ├── arrow-left.png │ ├── arrow-right.png │ ├── attach-1.png │ ├── attach-2.png │ ├── back_disabled.png │ ├── back_enabled.png │ ├── back_enabled_hover.png │ ├── bg.jpg │ ├── chosen-sprite.png │ ├── chosen-sprite@2x.png │ ├── cloud.png │ ├── clouds.png │ ├── eiffel.jpg │ ├── forward_disabled.png │ ├── forward_enabled.png │ ├── forward_enabled_hover.png │ ├── image-1.jpg │ ├── image-2.jpg │ ├── image-3.jpg │ ├── image-4.jpg │ ├── image-5.jpg │ ├── logo-collapsed.png │ ├── logo-collapsed2x.png │ ├── logo-white-bg.png │ ├── logo-white-bg@2x.png │ ├── logo.png │ ├── logo2x.png │ ├── minus.png │ ├── news-image-widget-2.png │ ├── news-image-widget-3.png │ ├── news-image-widget-4.png │ ├── news-image-widget.png │ ├── notes-lines.png │ ├── ok-white-full.png │ ├── ok-white.png │ ├── ok.png │ ├── pic1.png │ ├── pic2.png │ ├── pic3.jpg │ ├── pic4.jpg │ ├── plus-white.png │ ├── plus.png │ ├── sample.jpg │ ├── sort_asc.png │ ├── sort_asc_disabled.png │ ├── sort_both.png │ ├── sort_desc.png │ ├── sort_desc_disabled.png │ ├── spritemap.png │ ├── spritemap@2x.png │ ├── switch.png │ ├── user-1.png │ ├── user-2.png │ ├── user-3.png │ ├── user-4.png │ └── user-5.png ├── javascripts │ ├── .DS_Store │ ├── TweenMax.min.js │ ├── bootstrap.min.js │ ├── dropzone.min.js │ ├── globalize.min.js │ ├── highchartThemes.js │ ├── highcharts.js │ ├── initialize-tooltips.js │ ├── joinable.js │ ├── jquery-1.11.1.min.js │ ├── jquery-migrate-1.2.1.min.js │ ├── jquery-ui-1.9.2.custom.min.js │ ├── jquery.dataTables.js │ ├── jquery.dataTables.min.js │ ├── jquery.edatatables.js │ ├── jquery.form.js │ ├── jquery.min.js │ ├── jquery.nicescroll.js │ ├── progress.js │ ├── push.js │ ├── resizeable.js │ ├── scripts.js │ ├── submited.js │ ├── sweetalert.min.js │ ├── task.js │ ├── tooltipster.bundle.min.js │ ├── xenon-api.js │ ├── xenon-custom.js │ ├── xenon-toggles.js │ ├── xenon-widgets.js │ └── zeroModal.js └── stylesheets │ ├── .DS_Store │ ├── DT_bootstrap.css │ ├── app.css │ ├── bootstrap-reset.css │ ├── bootstrap.css │ ├── bootstrap.min.css │ ├── custom-ico-fonts.css │ ├── custom.css │ ├── datapp.css │ ├── dropzone.css │ ├── error.css │ ├── font-awesome.css │ ├── font-awesome.min.css │ ├── google.css │ ├── jarcustom.css │ ├── jquery-ui.min.css │ ├── lines.css │ ├── main.css │ ├── material.min.css │ ├── meteocons.css │ ├── style-responsive.css │ ├── style.css │ ├── sweetalert.css │ ├── tooltipster-sideTip-punk.min.css │ ├── tooltipster.bundle.min.css │ ├── xenon-components.css │ ├── xenon-core.css │ ├── xenon-forms.css │ ├── xenon-skins.css │ └── zeroModal.css ├── spark-submit-ui.iml └── test ├── ApplicationSpec.scala └── IntegrationSpec.scala /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore Play! working directory # 2 | bin/ 3 | .eclipse 4 | /lib/ 5 | /logs/ 6 | # Created by .ignore support plugin (hsz.mobi) 7 | ### PlayFramework template 8 | # Ignore Play! working directory # 9 | bin/ 10 | .eclipse 11 | /lib/ 12 | /modules 13 | /project/target 14 | tmp/ 15 | test-result 16 | server.pid 17 | *.eml 18 | /dist/ 19 | .cache 20 | target/ 21 | ### JetBrains template 22 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 23 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 24 | 25 | # User-specific stuff: 26 | .idea/workspace.xml 27 | .idea/tasks.xml 28 | .idea/dictionaries 29 | .idea/vcs.xml 30 | .idea/jsLibraryMappings.xml 31 | .idea 32 | 33 | 34 | # Sensitive or high-churn files: 35 | .idea/dataSources.ids 36 | .idea/dataSources.xml 37 | .idea/dataSources.local.xml 38 | .idea/sqlDataSources.xml 39 | .idea/dynamic.xml 40 | .idea/uiDesigner.xml 41 | 42 | # Gradle: 43 | .idea/gradle.xml 44 | .idea/libraries 45 | 46 | # Mongo Explorer plugin: 47 | .idea/mongoSettings.xml 48 | 49 | ## File-based project format: 50 | *.iws 51 | 52 | ## Plugin-specific files: 53 | 54 | # IntelliJ 55 | /out/ 56 | 57 | # mpeltonen/sbt-idea plugin 58 | .idea_modules/ 59 | 60 | # JIRA plugin 61 | atlassian-ide-plugin.xml 62 | 63 | # Crashlytics plugin (for Android Studio and IntelliJ) 64 | com_crashlytics_export_strings.xml 65 | crashlytics.properties 66 | crashlytics-build.properties 67 | fabric.properties 68 | 69 | ### SBT template 70 | # Simple Build Tool 71 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control 72 | 73 | /.idea 74 | /.settings 75 | /.sbtserver 76 | target/ 77 | /app-2.10 78 | lib_managed/ 79 | src_managed/ 80 | project/boot/ 81 | .history 82 | .cache 83 | ### JetBrains template 84 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 85 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 86 | 87 | # User-specific stuff: 88 | .idea/workspace.xml 89 | .idea/tasks.xml 90 | .idea/dictionaries 91 | .idea/vcs.xml 92 | .idea/jsLibraryMappings.xml 93 | 94 | # Sensitive or high-churn files: 95 | .idea/dataSources.ids 96 | .idea/dataSources.xml 97 | .idea/dataSources.local.xml 98 | .idea/sqlDataSources.xml 99 | .idea/dynamic.xml 100 | .idea/uiDesigner.xml 101 | 102 | # Gradle: 103 | .idea/gradle.xml 104 | .idea/libraries 105 | 106 | # Mongo Explorer plugin: 107 | .idea/mongoSettings.xml 108 | 109 | ## File-based project format: 110 | *.iws 111 | 112 | ## Plugin-specific files: 113 | 114 | # IntelliJ 115 | /out/ 116 | 117 | # mpeltonen/sbt-idea plugin 118 | .idea_modules/ 119 | 120 | # JIRA plugin 121 | atlassian-ide-plugin.xml 122 | 123 | # Crashlytics plugin (for Android Studio and IntelliJ) 124 | com_crashlytics_export_strings.xml 125 | crashlytics.properties 126 | crashlytics-build.properties 127 | fabric.properties 128 | ### PlayFramework template 129 | # Ignore Play! working directory # 130 | bin/ 131 | .eclipse 132 | /lib/ 133 | /modules 134 | /project/target 135 | /target 136 | tmp/ 137 | test-result 138 | server.pid 139 | *.eml 140 | /dist/ 141 | .cache 142 | ### Scala template 143 | *.class 144 | *.log 145 | 146 | # sbt specific 147 | .cache 148 | .history 149 | .lib/ 150 | dist/* 151 | target/ 152 | lib_managed/ 153 | src_managed/ 154 | project/boot/ 155 | project/plugins/project/ 156 | 157 | # Scala-IDE specific 158 | .scala_dependencies 159 | .worksheet 160 | 161 | 162 | #/conf/web-site.conf 163 | project/project/ 164 | 165 | /db/ 166 | -------------------------------------------------------------------------------- /Global.scala: -------------------------------------------------------------------------------- 1 | import play.api._ 2 | import play.api.mvc._ 3 | import play.api.mvc.Results._ 4 | 5 | import scala.concurrent.Future 6 | import play.api.Play.current 7 | import com.google.inject._ 8 | import models._ 9 | 10 | object Global extends GlobalSettings { 11 | 12 | private lazy val injector = { 13 | Play.isProd match { 14 | case _ => { 15 | Guice.createInjector(new Depend) 16 | } 17 | } 18 | } 19 | 20 | 21 | // 500 - internal server error 22 | override def onError(request: RequestHeader,ex: Throwable) = { 23 | Future.successful( 24 | InternalServerError( 25 | views.html.errors.fzf() 26 | ) 27 | ) 28 | } 29 | 30 | // 404 - page not found error 31 | override def onHandlerNotFound(request: RequestHeader)= { 32 | Future.successful( 33 | NotFound( 34 | views.html.errors.fzf() 35 | ) 36 | ) 37 | } 38 | 39 | override def getControllerInstance[A](clazz: Class[A]) = { 40 | injector.getInstance(clazz) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # spark-submit-ui 3 | >这是一个基于playframwork开发,web管理的spark应用程序 4 | 5 | >你需要安装SBT和Java以及PlayFramowrk。项目基于2.2.x 版本开发,需要PlayFramowrk 2.2或更高版本。 6 | 7 | #### 测试环境 8 | * JDK8 9 | * Center OS 6.5 10 | * Spark 1.5.2 11 | * Hadoop 2.6.0 12 | * Scala 2.11 13 | 14 | #### 主要功能 15 | * hadoop metrics 数据监控 16 | * spark 集群状态信息展示 17 | * 完善的spark app 提交与管理 18 | * 任务状态监控,状态推送 19 | 20 | #### 并下载并安装Play Framework 编译环境 21 | [Installing Play](https://www.playframework.com/documentation/2.5.x/Installing") 22 | 23 | 24 | #### 修改配置文件,将集群地址替换为你的 25 | 文件路径在 26 |
conf/web-site.conf
27 | ### 编译与运行 28 | 然后去 http://localhost:9000 查看正在运行的服务器。 29 | 30 | 如果运行有这个界面提示,点击Apply this script now 初始化数据表 31 | ![](http://upload-images.jianshu.io/upload_images/522641-65dbf16c874c1289.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 32 | 33 | #### 编译与运行 34 |
 activator run 
35 | 36 | #### 项目默认使用H2数据库 37 | 这是Play 内嵌的一个数据库 H2 38 | H2官方介绍 http://www.h2database.com/html/main.html 39 | 40 | 如果想要换成Mysql或者是其他的存储可以参考指引 41 | MySQL 数据库引擎连接属性 42 | 配置文件 conf/application.conf 43 | 44 |
 
45 | db.default.driver=com.mysql.jdbc.Driver
46 | db.default.url="jdbc:mysql://localhost/playdb"
47 | db.default.user=playdbuser
48 | db.default.pass="a strong password" 
49 | 50 | 51 | #其他 52 | 53 | 通过界面管理,kill或者rerun任务 54 | 55 | ![](http://upload-images.jianshu.io/upload_images/522641-8bc5a35a895f944e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 56 | 57 | 如果你的提交参数或配置导致异常,可以在提交时查看相关的错误输出 58 | 59 | ![](https://github.com/kingekinge/spark-submit-ui/blob/master/public/images/522641-46907f6c8f3b2b0b.png) 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /app/controllers/Application.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | import java.text.SimpleDateFormat 3 | import java.util.Date 4 | 5 | import akka.actor.ActorRef 6 | import com.google.inject.Inject 7 | import models._ 8 | import models.io.{Message, MessageList, TaskMessage} 9 | import models.metrics.MetricsProvider 10 | import play.api.libs.iteratee.Concurrent.Channel 11 | import play.api.libs.iteratee.{Concurrent, Enumerator, Iteratee} 12 | import play.api.libs.json.{JsValue, Json} 13 | import play.api.mvc._ 14 | import play.libs.Akka 15 | import akka.actor._ 16 | import akka.event.Logging 17 | import akka.pattern.ask 18 | import akka.util.Timeout 19 | import play.api.Logger 20 | import play.api.mvc._ 21 | 22 | import scala.concurrent.duration._ 23 | import scala.concurrent.{ExecutionContext, Future} 24 | import scala.collection.mutable 25 | import scala.concurrent.ExecutionContext.Implicits.global 26 | import scala.concurrent.stm.{Sink, Source} 27 | 28 | /** 29 | * WebSocket and Index 30 | */ 31 | class Application @Inject()(metricsProvider: MetricsProvider)(execute: Execute) extends Controller with Secured { 32 | 33 | 34 | def index = IsAuthenticated { username => implicit request => 35 | val rpcInfo: RPCInfo = metricsProvider.getMetricMouled[RPCInfo](RPCInfo.getClass) 36 | val dfsInfo: DFSInfo = metricsProvider.getMetricMouled[DFSInfo](DFSInfo.getClass) 37 | val memInfo: MEMInfo = metricsProvider.getMetricMouled[MEMInfo](MEMInfo.getClass) 38 | Ok(views.html.index(rpcInfo,dfsInfo,memInfo,getNowDate)) 39 | } 40 | 41 | 42 | def startpush = WebSocket.using[String] { implicit request => 43 | val user: String = request.session.get("email").get 44 | val (out,channel) = Concurrent.broadcast[String] 45 | val in = Iteratee.foreach[String] { msg => 46 | execute.register(user,request.id.toString,channel) 47 | } 48 | (in,out) 49 | } 50 | 51 | 52 | 53 | 54 | 55 | 56 | def msglist =IsAuthenticated { username => implicit request => 57 | implicit val residentWrites = Json.writes[TaskMessage] 58 | implicit val clusterListWrites = Json.writes[MessageList] 59 | val json: JsValue = Json.toJson(MessageList(Message.getMessages(username))) 60 | Ok(json) 61 | } 62 | 63 | def read(appId:String)=Action{ 64 | Message.deleteMessage(appId) 65 | Ok(views.html.tasklist()) 66 | } 67 | 68 | def readall=IsAuthenticated{ username => implicit request => 69 | Message.deleteAllMessage(username) 70 | Ok(views.html.tasklist()) 71 | } 72 | 73 | private[this] def getNowDate:String ={ 74 | val before = 3*60*60*1000 75 | val currentTime = new Date(System.currentTimeMillis()-before) 76 | currentTime.setMinutes(0) 77 | val formatter = new SimpleDateFormat("yyyy,MM,dd,HH,mm,ss") 78 | formatter.format(currentTime) 79 | } 80 | 81 | 82 | 83 | 84 | 85 | } 86 | -------------------------------------------------------------------------------- /app/controllers/apps/SparkList.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api.mvc.{Action, BodyParsers, Controller} 4 | import com.google.inject.Inject 5 | import models.TaskDataProvider.{AppDataObject, SparkTaskList, TaskData, YarnTaskInfoList} 6 | import models.TaskInfo 7 | import models.utils.Config 8 | import play.api.libs.json._ 9 | 10 | import scala.language.postfixOps 11 | import scala.io.Source 12 | 13 | /** 14 | * Created by kinge 15 | */ 16 | class SparkList @Inject()(conf:Config) extends Controller { 17 | 18 | 19 | def getSparkInfo=Action{ 20 | Ok(Source.fromURL("http://"+conf.getString("spark.master.host")+"/json").mkString) 21 | } 22 | 23 | def sparklist=Action{ 24 | Ok(views.html.sparklist()) 25 | } 26 | 27 | 28 | 29 | import models.TaskDataProvider.taskListReads 30 | implicit val taskWrites = Json.writes[TaskInfo] 31 | implicit val residentWrites = Json.writes[SparkTaskList] 32 | 33 | 34 | def getSaprkApps=Action{ 35 | val json= Source.fromURL("http://"+conf.getString("spark.master.host")+"/json").mkString 36 | val taskData: TaskData = Json.parse(json).as[TaskData] 37 | val apps: Seq[TaskInfo] = taskData.activeapps ++ taskData.completedapps 38 | Ok(Json.toJson(SparkTaskList(apps))) 39 | } 40 | 41 | 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/controllers/apps/YarnList.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | 4 | import com.google.inject.Inject 5 | import models.utils.Config 6 | import play.api.mvc._ 7 | import scala.io.Source 8 | 9 | /** 10 | * Created by kinge 11 | */ 12 | class YarnList @Inject()(conf:Config) extends Controller { 13 | 14 | def getYarnInfo=Action{ 15 | Ok(Source.fromURL("http://"+conf.getString("hadoop.yarn.host")+"/ws/v1/cluster/apps").mkString) 16 | } 17 | 18 | def yarnlist=Action{ 19 | Ok(views.html.yarnlist()) 20 | } 21 | 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/controllers/auth/Secured.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api.mvc._ 4 | 5 | /** 6 | * Provide security features 7 | */ 8 | trait Secured extends { 9 | 10 | /** 11 | * Retrieve the connected user's email 12 | */ 13 | private def username(request: RequestHeader) = request.session.get("email") 14 | 15 | /** 16 | * Not authorized, forward to login 17 | */ 18 | private def onUnauthorized(request: RequestHeader) = { 19 | Results.Redirect(routes.Authentication.login) 20 | } 21 | 22 | /** 23 | * Action for authenticated users. 24 | */ 25 | def IsAuthenticated(f: => String => Request[AnyContent] => Result) = { 26 | Security.Authenticated(username, onUnauthorized) { user => 27 | Action(request => f(user)(request)) 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/controllers/auth/userName.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import play.api.mvc.Controller 4 | import play.api.libs.json._ 5 | 6 | 7 | 8 | object UserName extends Controller with Secured { 9 | 10 | def userName = IsAuthenticated { username => implicit request => 11 | var shortName = username 12 | if (username.contains("@")){ 13 | shortName = username.split("@")(0) 14 | } 15 | Ok(Json.obj("user"->username,"shortName"->shortName)) 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/controllers/spark/SparkJar.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | 4 | 5 | import com.google.inject.Inject 6 | import models.JobManagerActor.{InvalidJar, JarStored} 7 | import models.TaskDataProvider.AppDataObject 8 | import models._ 9 | import play.api.Logger 10 | import play.api.data.Forms._ 11 | import play.api.data._ 12 | import play.api.mvc._ 13 | 14 | 15 | /** 16 | * Created by kinge on 16/6/22. 17 | */ 18 | class SparkJar @Inject() (taskDao: TaskDao,taskProvider: TaskProvider[AppDataObject],execute: Execute) extends Controller with Secured { 19 | 20 | val executeForm:Form[ExecuteModel] = Form{ 21 | mapping ( 22 | "master"->text, 23 | "executeClass"->text, 24 | "numExecutors"->text, 25 | "driverMemory"->text, 26 | "executorMemory"->text, 27 | "total-executor-cores"->text, 28 | "jarLocation"->text, 29 | "args1"->text 30 | )(ExecuteModel.apply)(ExecuteModel.unapply) 31 | } 32 | 33 | 34 | def upload = Action(parse.multipartFormData) { implicit request => 35 | request.body.file("file").map { jobFile => 36 | session.get("email").map { user => 37 | Logger.info("username=>"+user) 38 | execute.storeJar(user,jobFile) match { 39 | case JarStored(uri) => Redirect(routes.SparkJar.executejarpage()) 40 | case InvalidJar(error) => Logger.info(error); BadRequest(error) 41 | case _ => NotFound 42 | } 43 | }.getOrElse { 44 | Unauthorized("The user does not exist, please login again!") 45 | } 46 | }.getOrElse { 47 | Redirect(routes.SparkJar.errorpage("Upload failed")) 48 | } 49 | } 50 | 51 | def executejarpage = Action { implicit request => 52 | Ok(views.html.upload(executeForm)) 53 | } 54 | 55 | def executejar = IsAuthenticated { username => implicit request => 56 | executeForm.bindFromRequest.fold( 57 | formWithErrors => { 58 | formWithErrors.errors.map(x => Logger.info(x.message)) 59 | formWithErrors.globalError.map(x => Logger.info(x.message)) 60 | BadRequest(views.html.error(formWithErrors.toString)) 61 | }, 62 | executeArguments => { 63 | Logger.info("execution mode=>"+executeArguments.master) 64 | execute.main(executeArguments) 65 | match { 66 | case JobSubmitSuccess(id) => { 67 | taskDao.saveTaskArgs(executeArguments)(id) 68 | taskProvider.loadTaskInfo(AppDataObject(id,username)) 69 | Ok("Task submitted successfully!") 70 | } 71 | case JobRunExecption(error) => Ok(error); 72 | case _ => NotFound 73 | } 74 | } 75 | ) 76 | } 77 | 78 | 79 | def errorpage(error:String) =Action { 80 | Ok(views.html.error(error)) 81 | } 82 | 83 | 84 | } 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /app/controllers/spark/TaskManager.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import com.google.inject.Inject 4 | import models.TaskDataProvider.AppDataObject 5 | import models._ 6 | import models.utils.Config 7 | import play.api.Logger 8 | import play.api.libs.ws.WS 9 | import play.api.mvc.{Action, Controller} 10 | 11 | import scala.concurrent.ExecutionContext.Implicits.global 12 | import scala.language.postfixOps 13 | 14 | /** 15 | * Created by kinge on 16/8/18. 16 | * Management related tasks run time 17 | */ 18 | class TaskManager @Inject() (config: Config,taskProvider:TaskProvider[AppDataObject],taskDao: TaskDao,execute: Execute) extends Controller with Secured{ 19 | 20 | import play.api.libs.json._ 21 | import play.api.libs.functional.syntax._ 22 | 23 | def tasklist=Action{ 24 | Ok(views.html.tasklist()) 25 | } 26 | 27 | 28 | def standaloneInfo =IsAuthenticated{ 29 | username => implicit request => 30 | implicit val residentWrites = Json.writes[TaskInfo] 31 | implicit val clusterWrites = Json.writes[TaskList] 32 | val json: JsValue = Json.toJson(TaskList(taskDao.getTaskInfoList(username))) 33 | Ok(json) 34 | } 35 | 36 | 37 | 38 | def yarnInfo =IsAuthenticated{ 39 | username => implicit request => 40 | 41 | implicit val yarnWrites: Writes[YarnTaskInfo] = ( 42 | (__ \ "application_id").write[String] and 43 | (__ \ "name").write[String] and 44 | (__ \ "apptype").write[String] and 45 | (__ \ "queue").write[String] and 46 | (__ \ "starttime").write[Long] and 47 | (__ \ "state").write[String] and 48 | (__ \ "finishtime").write[Long] 49 | )(unlift(YarnTaskInfo.unapply)) 50 | 51 | implicit val clusterWrites = Json.writes[YarnTaskList] 52 | val json: JsValue = Json.toJson(YarnTaskList(taskDao.getYarnTaskList(username))) 53 | Ok(json) 54 | 55 | } 56 | 57 | def killTask(appId:String): Unit ={ 58 | //curl -v -X PUT -H "Content-Type: application/json" -d '{"state": "KILLED"}' 'http://localhost:8088/application_1489377540859_0013/state' 59 | WS.url(s"http://${config.getString("hadoop.yarn.host")}/ws/v1/cluster/apps/${appId}/state").withHeaders("Content-Type"->"application/json").put(Json.obj("state"->"KILLED")) map{ 60 | response => response.status match { 61 | case 200 => Some{ 62 | Logger.debug(s"post to kill ${appId} success" ) 63 | } 64 | case _ => None 65 | } 66 | } 67 | } 68 | 69 | 70 | 71 | def kill(appId:String) =IsAuthenticated{ 72 | username => implicit request => 73 | if(appId.startsWith("application")){ 74 | killTask(appId) 75 | }else{ 76 | val spark_master = config.getString("spark.master.host") 77 | WS.url("http://"+spark_master+"/app/kill/").withQueryString(("terminate","true")).withQueryString(("id",appId)).post("content") map{ 78 | response => response.status match { 79 | case 200 => Some{ 80 | Logger.debug(s"post to kill ${appId} success" ) 81 | } 82 | case _ => None 83 | } 84 | } 85 | } 86 | Ok("KILLED") 87 | } 88 | 89 | 90 | def rerun(appId:String) =IsAuthenticated{ 91 | username => implicit request => 92 | val executeModel: ExecuteModel = taskDao.getTaskArgs(appId) 93 | execute.main(executeModel) match { 94 | case JobSubmitSuccess(id) => { 95 | Logger.info(s"old Id====> $appId,new id====> $id") 96 | /** 97 | *Save the new task parameters add to the List 98 | */ 99 | taskDao.saveTaskArgs(executeModel)(id) 100 | taskProvider.coverTask(appId) 101 | taskProvider.loadTaskInfo(AppDataObject(id,username)); 102 | Ok(id) 103 | } 104 | case JobRunExecption(error) => Ok(error) 105 | case _ => NotFound 106 | } 107 | } 108 | 109 | 110 | 111 | 112 | 113 | } 114 | -------------------------------------------------------------------------------- /app/models/Mail/RegisterExecption.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | /** 4 | * Created by kinge on 16/6/17. 5 | */ 6 | abstract class EmailStatus(val ex:String) extends Exception(ex){ 7 | def unapply(arg: EmailStatus): Option[String] ={ Some(ex) } 8 | } 9 | 10 | case class EmailExecption(name:String) extends EmailStatus(name) 11 | case class VerifyException(name:String) extends EmailStatus(name) 12 | case class Success(name:String) extends EmailStatus(name) 13 | case class Failure(name:String) extends EmailStatus(name) 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/models/actor/ActorStack.scala: -------------------------------------------------------------------------------- 1 | package models.actor 2 | 3 | import akka.actor.Actor 4 | 5 | /** 6 | * Created by kinge on 16/7/4. 7 | */ 8 | trait ActorStack extends Actor { 9 | def wrappedReceive: Receive 10 | 11 | def receive: Receive = { 12 | case x => if (wrappedReceive.isDefinedAt(x)) wrappedReceive(x) else unhandled(x) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/models/actor/InstrumentedActor.scala: -------------------------------------------------------------------------------- 1 | package models.actor 2 | 3 | 4 | /** 5 | * Created by kinge on 16/7/4. 6 | */ 7 | abstract class InstrumentedActor extends ActorStack with Slf4jLogging { 8 | 9 | override def preRestart(reason: Throwable, message: Option[Any]) { 10 | super.preRestart(reason, message) 11 | } 12 | 13 | override def postStop() { logger.warn(getClass.getName) } 14 | } 15 | -------------------------------------------------------------------------------- /app/models/actor/Slf4jLogging.scala: -------------------------------------------------------------------------------- 1 | package models.actor 2 | 3 | import org.slf4j.LoggerFactory 4 | 5 | /** 6 | * Created by kinge 16/7/4. 7 | */ 8 | trait Slf4jLogging extends ActorStack { 9 | val logger = LoggerFactory.getLogger(getClass) 10 | private[this] val myPath = self.path.toString 11 | 12 | withAkkaSourceLogging { 13 | logger.info("Starting actor " + getClass.getName) 14 | } 15 | 16 | override def receive: Receive = { 17 | case x => 18 | withAkkaSourceLogging { 19 | super.receive(x) 20 | } 21 | } 22 | 23 | private def withAkkaSourceLogging(fn: => Unit) { 24 | try { 25 | org.slf4j.MDC.put("akkaSource", myPath) 26 | fn 27 | } finally { 28 | org.slf4j.MDC.remove("akkaSource") 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/models/actor/WebSocketChannel.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import akka.actor.{ActorPath, ActorRef, Props} 4 | import models.actor.InstrumentedActor 5 | import play.api.libs.iteratee.Concurrent.Channel 6 | import play.api.libs.iteratee.{Concurrent, Enumerator} 7 | 8 | case class Send(data:String) 9 | 10 | /** 11 | * Created by kinge on 16/8/18. 12 | */ 13 | object WebSocketChannel { 14 | 15 | 16 | def props(channel: Concurrent.Channel[String]):Props ={ 17 | Props(new WebSocketChannel(channel)) 18 | } 19 | } 20 | 21 | class WebSocketChannel(channel: Concurrent.Channel[String]) extends InstrumentedActor { 22 | 23 | override def wrappedReceive: Receive = { 24 | case Send(data) => channel.push(data) 25 | 26 | } 27 | } -------------------------------------------------------------------------------- /app/models/depend/Depend.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import com.tzavellas.sse.guice.ScalaModule 4 | import TaskDataProvider.AppDataObject 5 | import models.metrics.{HadoopMetricsProvider, MetricsProvider} 6 | import models.utils.{Config, Configuration} 7 | /** 8 | * Created by kinge on 16/8/22. 9 | */ 10 | class Depend extends ScalaModule{ 11 | override def configure(): Unit = { 12 | bind[TaskDao].to[TaskInfoDao] 13 | bind[TaskProvider[AppDataObject]].to[TaskDataProvider] 14 | bind[Config].to[Configuration] 15 | bind[MetricsProvider].to[HadoopMetricsProvider] 16 | bind[Execute].toInstance(new Execute) 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/models/deploy/CommonMessages.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | /** 4 | * Created by kinge on 16/7/11. 5 | * 6 | */ 7 | sealed trait CommonMessages 8 | 9 | /** Task submitted abnormal */ 10 | case class JobSubmitExecption(msg:String) extends RuntimeException(msg) with CommonMessages 11 | 12 | /** End of the task to run */ 13 | case class JobRunFinish(msg:String) extends RuntimeException(msg) with CommonMessages 14 | 15 | /** Task to run abnormal */ 16 | case class JobRunExecption(msg:String) extends RuntimeException(msg) with CommonMessages 17 | 18 | /** Task submitted successfully */ 19 | case class JobSubmitSuccess(msg:String) extends RuntimeException(msg) with CommonMessages 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/models/deploy/CreateBatchRequest.scala: -------------------------------------------------------------------------------- 1 | package models.deploy 2 | 3 | /** 4 | * Created by kinge on 16/7/13. 5 | */ 6 | class CreateBatchRequest { 7 | 8 | var master :Option[String]=None 9 | var file: String = _ 10 | var proxyUser: Option[String] = None 11 | var args: List[String] = List() 12 | var className: Option[String] = None 13 | var jars: List[String] = List() 14 | var pyFiles: List[String] = List() 15 | var files: List[String] = List() 16 | var driverMemory: Option[String] = None 17 | var driverCores: Option[String] = None 18 | var executorMemory: Option[String] = None 19 | var executorCores: Option[String] = None 20 | var total_executor_cores:Option[String] =None 21 | var numExecutors: Option[String] = None 22 | var jarLocation :Option[String] =None 23 | var archives: List[String] = List() 24 | var queue: Option[String] = None 25 | var name: Option[String] = None 26 | var conf: Map[String, String] = Map() 27 | 28 | } 29 | 30 | object CreateBatchRequest{ 31 | def apply(): CreateBatchRequest = new CreateBatchRequest() 32 | } -------------------------------------------------------------------------------- /app/models/deploy/Execute.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import java.util.UUID 4 | import java.util.concurrent.TimeUnit 5 | 6 | import akka.actor.{ActorPath, ActorRef, ActorSelection, ActorSystem, Props} 7 | import akka.util.Timeout 8 | import models.JobManagerActor.{Initializ, StoreJar, SubmitJob} 9 | import play.api.libs.Files.TemporaryFile 10 | import play.api.mvc.MultipartFormData.FilePart 11 | import akka.pattern.ask 12 | import models.deploy.StatusListener.PushStack 13 | import models.deploy.{CreateBatchRequest, StatusListener} 14 | import models.utils.{Config, Configuration} 15 | import play.api.libs.iteratee.Concurrent 16 | 17 | import scala.concurrent.duration.Duration 18 | import scala.concurrent._ 19 | /** 20 | * Created by kinge on 16/4/21. 21 | */ 22 | class Execute { 23 | 24 | private[this] var _actorSystem :ActorSystem= _ 25 | private[this] var _jobMange:ActorRef= _ 26 | private[this] val _config:Config =new Configuration 27 | private[this] val _task_dao :TaskDao =new TaskInfoDao 28 | private[this] val _dao: JobFileDAO = new JobFileDAO(_config) 29 | private[this] var _statusLister :ActorRef =_ 30 | 31 | def makeSystem ={ 32 | _actorSystem = ActorSystem("jobSystem") 33 | _statusLister=_actorSystem.actorOf(StatusListener.props(_config,_dao,_task_dao),"StatusLinster") 34 | _jobMange=_actorSystem.actorOf((JobManagerActor.props(_config,_dao,_task_dao,_statusLister)), "JobManger") 35 | _actorSystem.registerOnTermination(System.exit(0)) 36 | } 37 | 38 | makeSystem 39 | 40 | 41 | def register(user:String,id:String,channel: Concurrent.Channel[String])={ 42 | val webSocketChannel = _actorSystem.actorOf(WebSocketChannel.props(channel)) 43 | val actorPath: ActorPath = _actorSystem.actorOf(MessagePool.props(webSocketChannel),s"UserActor_$id").path 44 | _statusLister ! PushStack(user,actorPath) 45 | } 46 | 47 | 48 | private def getRequest(executeModel: ExecuteModel): CreateBatchRequest ={ 49 | val timeoutSecs: Long = _config.getLong("job.request.timeout.seconds") 50 | Await.result( (_jobMange ? Initializ(executeModel))(Timeout(timeoutSecs,TimeUnit.SECONDS)) , 51 | new Timeout(Duration.create(timeoutSecs,"seconds")).duration).asInstanceOf[CreateBatchRequest] 52 | } 53 | 54 | 55 | def main(executeModel: ExecuteModel)={ 56 | val timeoutSecs: Long = _config.getLong("job.submit.timeout.seconds") 57 | Await.result( 58 | (_jobMange ? SubmitJob(getRequest(executeModel))) 59 | (Timeout(timeoutSecs,TimeUnit.SECONDS)), 60 | new Timeout(Duration.create(timeoutSecs,"seconds")).duration) 61 | } 62 | 63 | 64 | def storeJar(userName:String,filePart: FilePart[TemporaryFile]):Any = { 65 | val timeoutSecs: Long = _config.getLong("job.upload.timeout.seconds") 66 | Await.result( (_jobMange ? StoreJar(userName,filePart)) 67 | (Timeout(timeoutSecs,TimeUnit.SECONDS)), 68 | new Timeout(Duration.create(timeoutSecs,"seconds")).duration); 69 | } 70 | 71 | 72 | 73 | } -------------------------------------------------------------------------------- /app/models/deploy/MatchEngine.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import models.utils.Configuration 4 | 5 | import scala.util.matching.Regex 6 | 7 | /** 8 | * Created by kinge on 16/8/18. 9 | */ 10 | object MatchEngine { 11 | 12 | val config: Configuration = new Configuration 13 | 14 | /** 15 | * on_local pattern matching 16 | */ 17 | val regex_on_local = """Starting executor ID driver on host localhost(.*)""".r.unanchored 18 | 19 | /** 20 | * on yarn-cluster pattern matching 21 | */ 22 | val regex_on_yarn = """Submitted application (.*)""".r.unanchored 23 | 24 | 25 | /** 26 | * standalone pattern matching 27 | */ 28 | val regex_on_standalone = """Spark cluster with app ID (.*)""".r.unanchored 29 | 30 | 31 | val spark_uri ="http://"+config.getString("spark.master.host")+"/json/" 32 | 33 | val yarn_uri="http://"+config.getString("hadoop.yarn.host")+"/ws/v1/cluster/apps" 34 | 35 | 36 | 37 | def matchMode :PartialFunction[String,Regex]={ 38 | case m if m.startsWith("yarn") => regex_on_yarn 39 | case m if m.startsWith("spark") => regex_on_standalone 40 | case m if m.startsWith("local") => regex_on_local 41 | } 42 | 43 | def matchURI : PartialFunction[String,Option[(String,String)]]={ 44 | case m if m.startsWith("application") => Some(("yarn",yarn_uri)) 45 | case m if m.startsWith("app") => Some(("standalone",spark_uri)) 46 | case m if m.startsWith("local") => None 47 | 48 | } 49 | 50 | } 51 | 52 | 53 | -------------------------------------------------------------------------------- /app/models/deploy/MessagePool.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import akka.actor.{ActorRef, AllForOneStrategy, PoisonPill, Props, Terminated} 4 | import akka.actor.SupervisorStrategy._ 5 | import models.actor.InstrumentedActor 6 | 7 | 8 | 9 | sealed trait State 10 | object RUNNING extends State 11 | object FINISHED extends State 12 | object KILLED extends State 13 | object FAILED extends State 14 | object NEW extends State 15 | 16 | 17 | 18 | /** 19 | * Created by kinge on 16/8/31. 20 | */ 21 | object MessagePool { 22 | def props(webSocketChannel: ActorRef): Props = Props(new MessagePool(webSocketChannel)) 23 | } 24 | 25 | 26 | class MessagePool(webSocketChannel: ActorRef) extends InstrumentedActor { 27 | 28 | override val supervisorStrategy = AllForOneStrategy() { 29 | case anyException => Stop 30 | } 31 | override def preStart()={ 32 | context.watch(webSocketChannel) 33 | } 34 | 35 | 36 | 37 | override def wrappedReceive: Receive = { 38 | case RUNNING => webSocketChannel ! Send("Job is running!") 39 | case FINISHED => webSocketChannel ! Send("Job finish!") 40 | case KILLED => webSocketChannel ! Send("Job is killed"); 41 | case FAILED => webSocketChannel ! Send("Job failure!") 42 | 43 | 44 | case t: Terminated => self ! PoisonPill 45 | 46 | } 47 | } -------------------------------------------------------------------------------- /app/models/deploy/StatusListener.scala: -------------------------------------------------------------------------------- 1 | package models.deploy 2 | 3 | import akka.actor.{ActorPath, PoisonPill, Props, Terminated} 4 | import models.actor.InstrumentedActor 5 | import models.deploy.StatusListener.{AppFinish, PushStack} 6 | import models.io.{Message, TaskMessage} 7 | import models.{FAILED, KILLED, _} 8 | import models.utils.Config 9 | import play.api.Logger 10 | 11 | 12 | 13 | /** 14 | * Created by kinge on 16/7/11. 15 | * Task status process 16 | */ 17 | object StatusListener{ 18 | 19 | var _paths =Map.empty[String,ActorPath] 20 | 21 | case class AppFinish(appId: String) 22 | case class PushStack(user:String,actorPath: ActorPath) 23 | case class AppRuning(msg:String) 24 | 25 | 26 | def props(config:Config,jobDAO: JobDAO,taskDao: TaskDao): Props = Props(classOf[StatusListener], config,jobDAO,taskDao) 27 | 28 | } 29 | 30 | 31 | class StatusListener(config:Config,jobDAO: JobDAO,taskDao: TaskDao) extends InstrumentedActor{ 32 | 33 | 34 | 35 | override def wrappedReceive: Receive = { 36 | case AppFinish(appId) => { 37 | 38 | if (appId.startsWith("application")) { 39 | val user: String = taskDao.findyarnTaskUser(appId) 40 | val act = context.system.actorSelection(StatusListener._paths(user)) 41 | taskDao.queryYarnState(appId).map { 42 | info => 43 | Message.addMessage(TaskMessage(info.application_id, info.state, user)) 44 | act ! Sealing(info.state); 45 | Logger.info(s"job finsh,current state==>" + info.state); 46 | } 47 | } else { 48 | val user = taskDao.findTaskUser(appId) 49 | val act = context.system.actorSelection(StatusListener._paths(user)) 50 | taskDao.queryState(appId).map { 51 | info => 52 | Message.addMessage(TaskMessage(info.app_id, info.state, user)); 53 | act ! Sealing(info.state); 54 | Logger.info(s"job finsh,current state==>" + info.state); 55 | } 56 | } 57 | } 58 | case PushStack(user,actorPath) => StatusListener._paths+=(user->actorPath) 59 | 60 | case t: Terminated => self ! PoisonPill 61 | } 62 | 63 | 64 | def Sealing:PartialFunction[String,State] ={ 65 | case m if(m.equals("RUNNING")) => RUNNING 66 | case m if(m.equals("FINISHED")) => FINISHED 67 | case m if(m.equals("KILLED")) => KILLED 68 | case m if(m.equals("FAILED")) => FAILED 69 | case _ => FINISHED 70 | 71 | } 72 | 73 | 74 | 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /app/models/deploy/package.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | /** 4 | * Created by kinge on 16/7/22. 5 | * Task submitted, deployment operations 6 | */ 7 | package object deploy { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /app/models/deploy/process/LineBufferedProcess.scala: -------------------------------------------------------------------------------- 1 | package models.deploy.process 2 | 3 | import akka.actor.ActorRef 4 | import org.apache.spark.Logging 5 | 6 | 7 | /** 8 | * Created by kinge on 16/7/12. 9 | */ 10 | class LineBufferedProcess(master: Option[String],act:ActorRef,process: Process) extends Logging { 11 | 12 | private[this] val _inputStream = new LineBufferedStream(master,act,process.getInputStream) 13 | private[this] val _errorStream = new LineBufferedStream(master,act,process.getErrorStream) 14 | 15 | def inputLines: IndexedSeq[String] = _inputStream.lines 16 | def errorLines: IndexedSeq[String] = _errorStream.lines 17 | 18 | def inputIterator: Iterator[String] = _inputStream.iterator 19 | def errorIterator: Iterator[String] = _errorStream.iterator 20 | 21 | def destroy(): Unit = { 22 | process.destroy() 23 | } 24 | 25 | def isAlive: Boolean = isProcessAlive(process) 26 | 27 | def exitValue(): Int = { 28 | process.exitValue() 29 | } 30 | 31 | def waitFor(): Int = { 32 | val returnCode = process.waitFor() 33 | _inputStream.waitUntilClose() 34 | _errorStream.waitUntilClose() 35 | returnCode 36 | } 37 | 38 | def isProcessAlive(process: Process): Boolean = { 39 | try { 40 | process.exitValue() 41 | false 42 | } catch { 43 | case _: IllegalThreadStateException => 44 | true 45 | } 46 | } 47 | } 48 | 49 | -------------------------------------------------------------------------------- /app/models/deploy/process/LineBufferedStream.scala: -------------------------------------------------------------------------------- 1 | package models.deploy.process 2 | 3 | import java.io.InputStream 4 | import java.util.UUID 5 | import java.util.concurrent.atomic.AtomicInteger 6 | import java.util.concurrent.locks.ReentrantLock 7 | 8 | import akka.actor.ActorRef 9 | import models.{JobSubmitSuccess, MatchEngine} 10 | import org.apache.spark.Logging 11 | import play.api.{Logger, cache} 12 | 13 | import scala.io.Source 14 | import scala.util.matching.Regex 15 | /** 16 | * Created by kinge on 16/7/12. 17 | * 18 | */ 19 | class LineBufferedStream(master:Option[String],act:ActorRef, inputStream: InputStream) extends Logging { 20 | 21 | private[this] var _lines: IndexedSeq[String] = IndexedSeq() 22 | 23 | private[this] val _lock = new ReentrantLock() 24 | private[this] val _condition = _lock.newCondition() 25 | private[this] var _finished = false 26 | private val atomicCounter = new AtomicInteger() 27 | 28 | def nextCount(): Int = atomicCounter.getAndIncrement() 29 | 30 | private val thread = new Thread { 31 | override def run() = { 32 | val lines:Iterator[String] = Source.fromInputStream(inputStream).getLines() 33 | val uid: String = UUID.randomUUID().toString 34 | val regex: Regex = MatchEngine.matchMode(master.get) 35 | 36 | for (line <- lines) { 37 | _lock.lock() 38 | line match { 39 | case regex(id) => act ! JobSubmitSuccess(id); 40 | case _ => Logger.info(line) 41 | } 42 | try { 43 | _lines = _lines :+ line 44 | _condition.signalAll() 45 | } finally { 46 | _lock.unlock() 47 | } 48 | } 49 | _lock.lock() 50 | try { 51 | _finished = true 52 | _condition.signalAll() 53 | } finally { 54 | _lock.unlock() 55 | } 56 | } 57 | } 58 | thread.setDaemon(true) 59 | thread.start() 60 | 61 | def lines: IndexedSeq[String] = _lines 62 | 63 | def iterator: Iterator[String] = { 64 | new LinesIterator 65 | } 66 | 67 | def waitUntilClose(): Unit = thread.join() 68 | 69 | private class LinesIterator extends Iterator[String] { 70 | private[this] var index = 0 71 | 72 | 73 | override def hasNext: Boolean = { 74 | if (index < _lines.length) { 75 | true 76 | } else { 77 | _lock.lock() 78 | try { 79 | if (_finished) { 80 | false 81 | } else { 82 | _condition.await() 83 | index < _lines.length 84 | } 85 | } finally { 86 | _lock.unlock() 87 | } 88 | } 89 | } 90 | 91 | override def next(): String = { 92 | val line = _lines(index) 93 | index += 1 94 | line 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /app/models/deploy/process/SparkProcessBuilder.scala: -------------------------------------------------------------------------------- 1 | 2 | package models.deploy.process 3 | 4 | import akka.actor.ActorRef 5 | import org.apache.spark.Logging 6 | import play.api.Logger 7 | 8 | import scala.collection.JavaConverters._ 9 | import scala.collection.immutable.HashMap.HashMap1 10 | import scala.collection.mutable 11 | import scala.collection.mutable.ArrayBuffer 12 | 13 | 14 | /** 15 | * Created by kinge on 16/7/12. 16 | */ 17 | class SparkProcessBuilder(act:ActorRef) extends Logging { 18 | 19 | private[this] var _executable: String = _ 20 | private[this] var _master: Option[String] = None 21 | private[this] var _deployMode: Option[String] = None 22 | private[this] var _className: Option[String] = None 23 | private[this] var _executor_memory: Option[String]=None 24 | private[this] var _num_executors: Option[String] = None 25 | private[this] var _driver_memory: Option[String] = None 26 | private[this] var _total_executor_cores: Option[String] = None 27 | private[this] var _name: Option[String] = Some("app") 28 | private[this] var _jarLocation:Option[String] =None 29 | private[this] var _queue: Option[String] = None 30 | private[this] var _redirectOutput: Option[ProcessBuilder.Redirect] = None 31 | private[this] var _redirectError: Option[ProcessBuilder.Redirect] = None 32 | private[this] var _redirectErrorStream: Option[Boolean] = None 33 | 34 | def jarLocation(jarLocation:String) :SparkProcessBuilder ={ 35 | _jarLocation =Some(jarLocation) 36 | this 37 | } 38 | 39 | def executable(executable: String): SparkProcessBuilder = { 40 | _executable = executable 41 | this 42 | } 43 | 44 | def master(masterUrl: String): SparkProcessBuilder = { 45 | _master = Some(masterUrl) 46 | this 47 | } 48 | 49 | def deployMode(deployMode: String): SparkProcessBuilder = { 50 | _deployMode = Some(deployMode) 51 | this 52 | } 53 | 54 | def className(className: String): SparkProcessBuilder = { 55 | _className = Some(className) 56 | this 57 | } 58 | 59 | def name(name: String): SparkProcessBuilder = { 60 | _name = Some(name) 61 | this 62 | } 63 | 64 | def executorCores(executorCores: String): SparkProcessBuilder = { 65 | _total_executor_cores = Some(executorCores) 66 | this 67 | } 68 | 69 | def executorMemory(executorMemory: String): SparkProcessBuilder = { 70 | _executor_memory =Some(executorMemory) 71 | this 72 | } 73 | 74 | def numExecutors(numExecutors: String): SparkProcessBuilder = { 75 | _num_executors=Some(numExecutors) 76 | this 77 | } 78 | 79 | def driverMemory(driverMemory: String): SparkProcessBuilder = { 80 | _driver_memory=Some(driverMemory) 81 | this 82 | } 83 | 84 | def queue(queue: String): SparkProcessBuilder = { 85 | _queue = Some(queue) 86 | this 87 | } 88 | 89 | def redirectOutput(redirect: ProcessBuilder.Redirect): SparkProcessBuilder = { 90 | _redirectOutput = Some(redirect) 91 | this 92 | } 93 | 94 | def redirectError(redirect: ProcessBuilder.Redirect): SparkProcessBuilder = { 95 | _redirectError = Some(redirect) 96 | this 97 | } 98 | 99 | def redirectErrorStream(redirect: Boolean): SparkProcessBuilder = { 100 | _redirectErrorStream = Some(redirect) 101 | this 102 | } 103 | 104 | def start(file: Option[String], args: Traversable[String]): LineBufferedProcess = { 105 | executable(file.get) 106 | var arguments = ArrayBuffer[String](_executable) 107 | 108 | def addOpt(option: String, value: Option[String]): Unit = { 109 | value.foreach { v => 110 | arguments += option 111 | arguments += v 112 | } 113 | } 114 | 115 | def addArg(value: Option[String]): Unit = { 116 | value.foreach{ 117 | v=> arguments += v 118 | } 119 | } 120 | 121 | def addList(values:Traversable[String]): Unit ={ 122 | if(values.nonEmpty){ 123 | values.foreach{ v => 124 | arguments += v 125 | } 126 | } 127 | } 128 | 129 | 130 | addOpt("--master", _master) 131 | addOpt("--class", _className) 132 | addOpt("--num-executors",_num_executors) 133 | addOpt("--executor-memory", _executor_memory) 134 | addOpt("--driver-memory",_driver_memory) 135 | addOpt("--total-executor-cores", _total_executor_cores) 136 | addArg(_jarLocation) 137 | addList(args) 138 | Logger.info(s"args $arguments") 139 | 140 | val pb = new ProcessBuilder(arguments.asJava) 141 | pb.redirectErrorStream(true) 142 | pb.redirectInput(java.lang.ProcessBuilder.Redirect.PIPE) 143 | val env = pb.environment() 144 | 145 | env.put("TEST_CLASSPATH", sys.props("java.class.path")) 146 | 147 | _redirectOutput.foreach(pb.redirectOutput) 148 | _redirectError.foreach(pb.redirectError) 149 | _redirectErrorStream.foreach(pb.redirectErrorStream) 150 | 151 | val process: LineBufferedProcess = new LineBufferedProcess(_master,act,pb.start()) 152 | process.waitFor() 153 | process 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /app/models/deploy/task/TaskProvider.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | /** 4 | * Created by king on 16/8/22. 5 | */ 6 | trait TaskProvider[T] { 7 | 8 | def coverTask(appId: String) 9 | 10 | def loadTaskInfo(app: T) 11 | 12 | def proTaskOnMaster(app: T) 13 | 14 | def proTaskOnYarn(app: T) 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /app/models/io/JobDAO.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import org.joda.time.DateTime 4 | import play.api.libs.Files.TemporaryFile 5 | import play.api.mvc.MultipartFormData.FilePart 6 | 7 | /** 8 | * Created by kinge on 16/7/11. 9 | */ 10 | case class JarInfo(userName: String, uploadTime: String, location:String) 11 | 12 | 13 | trait JobDAO { 14 | 15 | def saveJar(userName: String, uploadTime: DateTime, filePart: FilePart[TemporaryFile]):String 16 | 17 | 18 | } 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/models/io/JobFileDAO.scala: -------------------------------------------------------------------------------- 1 | package models 2 | import java.io.File 3 | 4 | import org.joda.time.DateTime 5 | import play.api.db.DB 6 | import play.api.libs.Files.TemporaryFile 7 | import play.api.mvc.MultipartFormData.FilePart 8 | import anorm._ 9 | import com.google.inject.Inject 10 | import models.utils.Config 11 | import play.api.Play.current 12 | 13 | import scala.collection.mutable 14 | 15 | /** 16 | * Created by kinge on 16/7/11. 17 | */ 18 | class JobFileDAO(config: Config) extends JobDAO { 19 | 20 | 21 | private val rootDir = config.getString("job.upload.path"); 22 | private val rootDirFile = new File(rootDir) 23 | private val rootOV = true 24 | private val apps = mutable.HashMap.empty[String, Seq[DateTime]] 25 | 26 | 27 | init 28 | 29 | private def init() { 30 | if (!rootDirFile.exists()) { 31 | if (!rootDirFile.mkdirs()) { 32 | throw new RuntimeException("Could not create directory " + rootDir) 33 | } 34 | } 35 | } 36 | 37 | 38 | override def saveJar(userName: String, uploadTime: DateTime, filePart: FilePart[TemporaryFile]):String= { 39 | val jobName:String=filePart.filename 40 | val file: File = new File(s"$rootDir$jobName") 41 | filePart.ref.moveTo(file,rootOV) 42 | addJar(jobName,uploadTime) 43 | file.getAbsoluteFile.getAbsolutePath 44 | } 45 | 46 | private def addJar(jobName: String, uploadTime: DateTime) { 47 | if (apps.contains(jobName)) { 48 | apps(jobName) = uploadTime +: apps(jobName) 49 | } else { 50 | apps(jobName) = Seq(uploadTime) 51 | } 52 | } 53 | 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /app/models/io/Message.scala: -------------------------------------------------------------------------------- 1 | package models.io 2 | 3 | import anorm.SqlParser._ 4 | import anorm._ 5 | import play.api.Play.current 6 | import play.api.db.DB 7 | 8 | /** 9 | * Created by kinge on 16/9/1. 10 | */ 11 | 12 | case class TaskMessage(id:String,state:String,user:String) 13 | case class MessageList(list:Seq[TaskMessage]) 14 | object Message { 15 | val taskmsg = { 16 | get[String]("task_msg.id") ~ 17 | get[String]("task_msg.state") ~ 18 | get[String]("task_msg.user") map { 19 | case id ~ state ~ user => TaskMessage(id,state,user) 20 | } 21 | } 22 | 23 | 24 | def addMessage(taskMessage: TaskMessage): TaskMessage ={ 25 | DB.withConnection { implicit connection => 26 | SQL( 27 | """ 28 | insert into task_msg values ( 29 | {id}, {state}, {user} 30 | ) 31 | """).on( 32 | 'id -> taskMessage.id, 33 | 'state -> taskMessage.state, 34 | 'user -> taskMessage.user 35 | ).executeUpdate() 36 | } 37 | taskMessage 38 | } 39 | 40 | def getMessages(user:String):Seq[TaskMessage]={ 41 | DB.withConnection { implicit connection => 42 | play.api.db.DB.withConnection { implicit connection => 43 | SQL("select * from task_msg where user={user}").on( 44 | 'user -> user 45 | ).as(taskmsg *) 46 | } 47 | } 48 | } 49 | 50 | def deleteMessage(appId:String)={ 51 | DB.withConnection { implicit connection => 52 | SQL( 53 | """ 54 | delete from task_msg where id={id} 55 | """).on( 56 | 'id -> appId 57 | ).executeUpdate() 58 | } 59 | } 60 | 61 | def deleteAllMessage(user:String)={ 62 | DB.withConnection { implicit connection => 63 | SQL( 64 | """ 65 | delete from task_msg where user={user} 66 | """).on( 67 | 'user -> user 68 | ).executeUpdate() 69 | } 70 | } 71 | 72 | 73 | 74 | 75 | } 76 | -------------------------------------------------------------------------------- /app/models/io/MetricsData.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import anorm.SqlParser._ 4 | import anorm._ 5 | import models.utils.Configuration 6 | import play.api.db.DB 7 | import play.api.Play.current 8 | 9 | /** 10 | * Created by kinge on 2017/2/17. 11 | */ 12 | case class MetricsData( 13 | receivedBytes:Long, 14 | sentBytes:Long, 15 | timestamp:Long, 16 | host:String, 17 | CapacityUsed:Long, 18 | CapacityRemaining:Long, 19 | CapacityUsedNonDFS:Long, 20 | MemNonHeapUsedM:Double, 21 | MemHeapUsedM:Double 22 | ) 23 | 24 | object MetricsData { 25 | 26 | val config: Configuration = new Configuration 27 | 28 | val metricsData ={ 29 | get[Long]("hadoop_metrics.ReceivedBytes")~ 30 | get[Long]("hadoop_metrics.SentBytes") ~ 31 | get[Long]("hadoop_metrics.timestamp") ~ 32 | get[String]("hadoop_metrics.host") ~ 33 | get[Long]("hadoop_metrics.CapacityUsed") ~ 34 | get[Long]("hadoop_metrics.CapacityRemaining") ~ 35 | get[Long]("hadoop_metrics.CapacityUsedNonDFS") ~ 36 | get[Double]("hadoop_metrics.MemNonHeapUsedM") ~ 37 | get[Double]("hadoop_metrics.MemHeapUsedM") map { 38 | case receivedBytes ~ 39 | sentBytes ~ 40 | timestamp ~ 41 | host ~ 42 | capacityUsed ~ 43 | capacityRemaining ~ 44 | capacityUsedNonDFS ~ 45 | memNonHeapUsedM ~ 46 | memHeapUsedM 47 | => 48 | MetricsData(receivedBytes,sentBytes,timestamp,host,capacityUsed,capacityRemaining,capacityUsedNonDFS,memNonHeapUsedM,memHeapUsedM) 49 | } 50 | } 51 | 52 | def updateMetrics(metricsData: MetricsData)={ 53 | play.api.db.DB.withConnection { implicit connection => 54 | SQL( 55 | """ 56 | insert into hadoop_metrics values ( 57 | {ReceivedBytes},{SentBytes},{host},{CapacityUsed},{CapacityRemaining},{CapacityUsedNonDFS},{MemHeapUsedM},{MemNonHeapUsedM},{timestamp} 58 | ) 59 | """).on( 60 | 'ReceivedBytes -> metricsData.receivedBytes, 61 | 'SentBytes -> metricsData.sentBytes, 62 | 'host -> metricsData.host, 63 | 'CapacityUsed -> metricsData.CapacityUsed, 64 | 'CapacityRemaining -> metricsData.CapacityRemaining, 65 | 'CapacityUsedNonDFS -> metricsData.CapacityUsedNonDFS, 66 | 'MemNonHeapUsedM -> metricsData.MemNonHeapUsedM, 67 | 'MemHeapUsedM -> metricsData.MemHeapUsedM, 68 | 'timestamp -> metricsData.timestamp 69 | ).executeUpdate() 70 | } 71 | metricsData 72 | } 73 | 74 | 75 | def getMetrics():Seq[MetricsData]={ 76 | DB.withConnection { implicit connection => 77 | play.api.db.DB.withConnection { implicit connection => 78 | SQL("select * from hadoop_metrics where host = {host} order by timestamp desc limit 0,36 ") 79 | .on('host -> config.getString("hadoop.metrics.host")).as(metricsData *) 80 | } 81 | } 82 | } 83 | 84 | 85 | 86 | 87 | 88 | 89 | } 90 | -------------------------------------------------------------------------------- /app/models/io/TaskDao.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import anorm.SqlParser._ 4 | import anorm.~ 5 | 6 | 7 | /** 8 | * Created by kinge on 16/8/22. 9 | */ 10 | case class TaskInfo( 11 | app_id:String, 12 | name:String, 13 | cores: Option[Int], 14 | memoryperslave:Long, 15 | state:String, 16 | starttime:Long, 17 | duration:Long 18 | ) 19 | 20 | case class YarnTaskInfo( 21 | application_id:String, 22 | name:String, 23 | apptype:String, 24 | queue:String, 25 | starttime:Long, 26 | state:String, 27 | finishtime:Long 28 | ) 29 | case class YarnTaskList(list:Seq[YarnTaskInfo]) 30 | case class TaskList(list:Seq[TaskInfo]) 31 | 32 | 33 | trait TaskDao{ 34 | 35 | val yarn = { 36 | get[String]("task_yarn.application_id") ~ 37 | get[String]("task_yarn.name") ~ 38 | get[String]("task_yarn.apptype")~ 39 | get[String]("task_yarn.queue") ~ 40 | get[Long]("task_yarn.starttime")~ 41 | get[String]("task_yarn.state")~ 42 | get[Long]("task_yarn.finishtime") map { 43 | case application_id ~ name ~ apptype ~ queue ~starttime ~state~finishtime=> YarnTaskInfo(application_id,name,apptype,queue,starttime,state,finishtime) 44 | } 45 | } 46 | 47 | val standalone = { 48 | get[String]("task_standalone.app_id") ~ 49 | get[String]("task_standalone.name") ~ 50 | get[Int]("task_standalone.cores")~ 51 | get[Long]("task_standalone.memoryperslave")~ 52 | get[String]("task_standalone.state") ~ 53 | get[Long]("task_standalone.starttime")~ 54 | get[Long]("task_standalone.duration") map { 55 | case app_id ~ name ~ cores ~ memoryperslave ~ state ~ starttime ~ duration => TaskInfo(app_id,name,Some(cores),memoryperslave,state,starttime,duration) 56 | } 57 | } 58 | 59 | 60 | val args = { 61 | get[String]("task_args.master") ~ 62 | get[String]("task_args.executeClass") ~ 63 | get[String]("task_args.numExecutors")~ 64 | get[String]("task_args.driverMemory")~ 65 | get[String]("task_args.executorMemory") ~ 66 | get[String]("task_args.total_executor_cores") ~ 67 | get[String]("task_args.jarLocation")~ 68 | get[String]("task_args.args1") map { 69 | case master ~ executeClass ~ numExecutors ~ driverMemory ~ executorMemory ~ total_executor_cores ~ jarLocation ~ args1 => ExecuteModel(master,executeClass,numExecutors,driverMemory,executorMemory,total_executor_cores,jarLocation,args1) 70 | } 71 | } 72 | 73 | 74 | def saveTaskArgs(executeModel: ExecuteModel)(appId:String) : ExecuteModel 75 | 76 | 77 | def getTaskArgs(appId:String):ExecuteModel 78 | 79 | 80 | def saveTask(task: TaskInfo)(user:String): TaskInfo 81 | 82 | def saveYarnTask(yarnTask: YarnTaskInfo)(user:String): YarnTaskInfo 83 | 84 | def getTaskInfoList(user:String): Seq[TaskInfo] 85 | 86 | def getYarnTaskList(user:String): Seq[YarnTaskInfo] 87 | 88 | def updateYarnTaskList(tasks: Seq[YarnTaskInfo]) 89 | 90 | def updateTaskList(tasks:Seq[TaskInfo]) 91 | 92 | def queryYarnState(appId:String):Option[YarnTaskInfo] 93 | 94 | def queryState(appId:String):Option[TaskInfo] 95 | 96 | def rmYarnTaskInfo(appId:String) 97 | 98 | def rmTaskInfo(appId:String) 99 | 100 | def findTaskUser(appId:String):String 101 | 102 | def findyarnTaskUser(appId:String):String 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | } -------------------------------------------------------------------------------- /app/models/io/package.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | /** 4 | * Database related operations 5 | * Created by kinge on 16/7/6. 6 | */ 7 | package object io { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /app/models/metrics/BaseInfo.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import play.api.libs.json.JsValue 4 | 5 | /** 6 | * Created by kinge on 2016/8/21. 7 | */ 8 | sealed class BaseInfo; 9 | /** 10 | * RPC INFO 11 | * @param rec_rpc 12 | * @param sent_rpc 13 | */ 14 | case class RPCInfo(rec_rpc:JsValue, 15 | sent_rpc :JsValue 16 | ) extends BaseInfo 17 | 18 | /** 19 | * HDFS 20 | * @param capacityUsed 21 | * @param capacityRemaining 22 | * @param capacityUsedNonDFS 23 | */ 24 | case class DFSInfo(capacityUsed:JsValue, 25 | capacityRemaining:JsValue, 26 | capacityUsedNonDFS:JsValue 27 | ) extends BaseInfo 28 | 29 | /** 30 | * MEM 31 | * @param memHeapUsedM 32 | * @param memNonHeapUsedM 33 | */ 34 | case class MEMInfo(memHeapUsedM:JsValue,memNonHeapUsedM:JsValue) extends BaseInfo 35 | 36 | 37 | case class BadInfo(msg:String)extends BaseInfo{ 38 | 39 | override def toString: String = this.msg 40 | 41 | 42 | } 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app/models/metrics/HadoopMetricsProvider.scala: -------------------------------------------------------------------------------- 1 | package models.metrics 2 | 3 | import akka.actor.Cancellable 4 | import models.{BaseInfo, MetricsData, RPCInfo} 5 | import models.TaskDataProvider.{AppDataObject, TaskData, YarnTaskInfoList} 6 | 7 | import scala.concurrent.ExecutionContext.Implicits.global 8 | import scala.language.postfixOps 9 | import models.utils.{Config, Configuration} 10 | import play.api.libs.json.{JsValue, Json} 11 | import play.api.libs.ws.WS 12 | import play.libs.Akka 13 | 14 | import scala.io.{BufferedSource, Source} 15 | 16 | /** 17 | * Created by kinge on 2016/6/7. 18 | */ 19 | class HadoopMetricsProvider extends MetricsProvider{ 20 | 21 | private[this] val _config:Config =new Configuration 22 | private[this] val _factory: Factory = new MetricsFactory 23 | 24 | val startMetrics: Cancellable = scheduleMetricsDate 25 | 26 | 27 | 28 | private def getMENInfo(json:JsValue)={ 29 | val memNonHeapUsedM = (json \\ "MemNonHeapUsedM")(0).as[Double] 30 | val memHeapUsedM = (json \\ "MemHeapUsedM")(0).as[Double] 31 | (memNonHeapUsedM,memHeapUsedM) 32 | } 33 | 34 | 35 | private def getDFSInfo(json:JsValue)={ 36 | val CapacityUsed = (json \\ "CapacityUsed")(0).as[Long] 37 | val CapacityRemaining = (json \\ "CapacityRemaining")(0).as[Long] 38 | val CapacityUsedNonDFS=(json \\ "CapacityUsedNonDFS")(0).as[Long] 39 | (CapacityUsed,CapacityRemaining,CapacityUsedNonDFS) 40 | } 41 | 42 | 43 | 44 | private def getRpcInfo(json:JsValue)={ 45 | val receivedBytes = (json \\ "ReceivedBytes")(0).as[Long] 46 | val sentBytes = (json \\ "SentBytes")(0).as[Long] 47 | (receivedBytes,sentBytes) 48 | } 49 | 50 | 51 | 52 | import scala.concurrent.duration._ 53 | 54 | private[this] def scheduleMetricsDate={ 55 | Akka.system.scheduler.schedule(0.second, _config.getLong("metrics.data-update.interval-ms") millis, new Runnable { 56 | override def run(): Unit = { 57 | val url="http://"+_config.getString("hadoop.metrics.host")+":50070/jmx" 58 | WS.url(url).get() map{ 59 | response => response.status match { 60 | case 200 => Some{ 61 | 62 | val (receivedBytes,sentBytes) = getRpcInfo(response.json) 63 | val (capacityUsed,capacityRemaining,capacityUsedNonDFS)=getDFSInfo(response.json) 64 | val (memNonHeapUsedM,memHeapUsedM)=getMENInfo(response.json) 65 | 66 | 67 | MetricsData.updateMetrics(MetricsData( 68 | receivedBytes, 69 | sentBytes, 70 | System.currentTimeMillis(), 71 | _config.getString("hadoop.metrics.host"), 72 | capacityUsed, 73 | capacityRemaining, 74 | capacityUsedNonDFS, 75 | memNonHeapUsedM, 76 | memHeapUsedM)) 77 | } 78 | case _ => None 79 | } 80 | } 81 | 82 | } 83 | }) 84 | } 85 | 86 | override def getMetricMouled[T](clz: Class[_]): T = { 87 | _factory.queryMetrics(clz) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /app/models/metrics/MetricsFactory.scala: -------------------------------------------------------------------------------- 1 | package models.metrics 2 | 3 | import models._ 4 | import play.api.Logger 5 | import play.api.libs.json.{JsValue, Json} 6 | 7 | /** 8 | * Created by kinge on 2016/7/23. 9 | *

metric data computing related @host:port/jmx

10 | * 11 | */ 12 | private [metrics] abstract class Factory { 13 | var metrics: Seq[MetricsData] =_ 14 | 15 | 16 | def queryMetrics[T](clz:Class[_]) :T={ 17 | metrics = MetricsData.getMetrics() 18 | clz.getClass match { 19 | case _=> queryMetricsModels(clz).asInstanceOf[T] 20 | } 21 | } 22 | 23 | def queryMetricsModels:PartialFunction[Class[_],BaseInfo] = ??? 24 | 25 | } 26 | 27 | private[metrics] class MetricsFactory extends Factory{ 28 | 29 | private[this] val BYTE_NUMBER=1024 30 | 31 | 32 | override def queryMetricsModels:PartialFunction[Class[_],BaseInfo]={ 33 | case x if x == RPCInfo.getClass => 34 | RPCInfo(ReceivedInterval, SentInterval) 35 | 36 | case x if x == DFSInfo.getClass => 37 | DFSInfo(CapacityUsed,CapacityRemaining,CapacityUsedNonDFS) 38 | 39 | case x if x==MEMInfo.getClass => 40 | MEMInfo(MemHeapUsedM,MemNonHeapUsedM) 41 | 42 | case _ => BadInfo("error") 43 | } 44 | 45 | 46 | 47 | private [this] def MemNonHeapUsedM={ 48 | val result:Seq[Double]={ 49 | for(me<- metrics) yield (me.MemNonHeapUsedM) 50 | } 51 | Json.toJson(result.reverse) 52 | } 53 | 54 | 55 | private [this] def MemHeapUsedM ={ 56 | val result:Seq[Double]={ 57 | for(me<- metrics) yield (me.MemHeapUsedM) 58 | } 59 | Json.toJson(result.reverse) 60 | } 61 | 62 | 63 | private [this] def CapacityUsed = { 64 | val result:Seq[Long]={ 65 | for(me<- metrics) yield (me.CapacityUsed / (BYTE_NUMBER*BYTE_NUMBER*BYTE_NUMBER)) 66 | } 67 | Json.toJson(result.reverse) 68 | } 69 | 70 | private [this] def CapacityRemaining ={ 71 | val result:Seq[Long]={ 72 | for(me<- metrics) yield (me.CapacityRemaining / (BYTE_NUMBER*BYTE_NUMBER*BYTE_NUMBER)) 73 | } 74 | Json.toJson(result.reverse) 75 | } 76 | 77 | private [this] def CapacityUsedNonDFS ={ 78 | val result:Seq[Long]={ 79 | for(me<- metrics) yield (me.CapacityUsedNonDFS / (BYTE_NUMBER*BYTE_NUMBER*BYTE_NUMBER)) 80 | } 81 | Json.toJson(result.reverse) 82 | } 83 | 84 | 85 | 86 | private[this] def SentInterval ={ 87 | val result:Seq[Long]={ 88 | for(i<- 0 until metrics.length) 89 | yield if(i==metrics.length-1){ 90 | 0 91 | }else{ 92 | (metrics(i).sentBytes-metrics(i+1).sentBytes)/BYTE_NUMBER 93 | } 94 | }.dropRight(1) 95 | Json.toJson(result.reverse) 96 | } 97 | 98 | 99 | private[this] def ReceivedInterval ={ 100 | val result:Seq[Long]={ 101 | for(i<- 0 until metrics.length) 102 | yield if(i==metrics.length-1){ 103 | 0 104 | }else{ 105 | (metrics(i).receivedBytes-metrics(i+1).receivedBytes)/BYTE_NUMBER 106 | } 107 | }.dropRight(1) 108 | Json.toJson(result.reverse) 109 | } 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | } 123 | -------------------------------------------------------------------------------- /app/models/metrics/MetricsProvider.scala: -------------------------------------------------------------------------------- 1 | package models.metrics 2 | 3 | /** 4 | * Created by kinge on 2016/6/2. 5 | */ 6 | trait MetricsProvider { 7 | 8 | def getMetricMouled[T](clz:Class[_]):T 9 | 10 | } 11 | -------------------------------------------------------------------------------- /app/models/shell/kill_job.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | arg1=$1 4 | echo "will kill the job with id =$arg1" 5 | exec yarn application -kill $arg1 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/models/user/User.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import anorm.SqlParser._ 4 | import anorm._ 5 | import play.api.Play.current 6 | import play.api.db.DB 7 | 8 | case class User(email: String, name: String, password: String,status:Int =0,audit:Int=0) 9 | case class Registration(email:String,name:String,password:String,repassword:String) 10 | case class Verify(email:String,captcha:String,name:String) 11 | case class UserList(all:Seq[User]) 12 | 13 | object User { 14 | 15 | /** 16 | * Parse a User from a ResultSet 17 | */ 18 | val simple = { 19 | get[String]("user.email") ~ 20 | get[String]("user.name") ~ 21 | get[String]("user.password")~ 22 | get[Int]("user.status") map { 23 | case email ~ name ~ password ~ status => User(email, name, password,status) 24 | } 25 | } 26 | 27 | 28 | 29 | /** 30 | * Retrieve all users. 31 | */ 32 | def findAll: Seq[User] = { 33 | DB.withConnection { implicit connection => 34 | play.api.db.DB.withConnection { implicit connection => 35 | SQL("select * from user").as(User.simple *) 36 | } 37 | } 38 | } 39 | 40 | /** 41 | * Authenticate a User. 42 | */ 43 | def authenticate(email: String, password: String): Option[User] = { 44 | DB.withConnection { implicit connection => 45 | play.api.db.DB.withConnection { implicit connection => 46 | SQL( 47 | """ 48 | select * from user where 49 | email = {email} and password = {password} 50 | """).on( 51 | 'email -> email, 52 | 'password -> MD5Utils.encode2hex(password)).as(User.simple.singleOpt) 53 | } 54 | } 55 | } 56 | 57 | 58 | 59 | /** 60 | * Authenticate a User. 61 | */ 62 | def hasUser(email: String): Boolean = { 63 | DB.withConnection { implicit connection => 64 | play.api.db.DB.withConnection { implicit connection => 65 | SQL( 66 | """ 67 | select * from user where 68 | email = {email} 69 | """).on( 70 | 'email -> email 71 | ).as(User.simple.singleOpt) 72 | } 73 | }.isDefined 74 | } 75 | 76 | 77 | def isActivate(email: String): Option[User] ={ 78 | play.api.db.DB.withConnection { implicit connection => 79 | SQL( 80 | """ 81 | select * from user where 82 | email = {email} and status = 1 83 | """).on( 84 | 'email -> email 85 | ). 86 | as(User.simple.singleOpt) 87 | } 88 | } 89 | 90 | 91 | 92 | def verifying(registration : Registration): User={ 93 | val newUser = User(registration.email, registration.name,registration.password) 94 | create(newUser) 95 | } 96 | 97 | /** 98 | * Retrieve a User from email. 99 | */ 100 | def findByEmail(email: String): Option[User] = { 101 | play.api.db.DB.withConnection { implicit connection => 102 | SQL("select * from user where email = {email}").on( 103 | 'email -> email).as(User.simple.singleOpt) 104 | } 105 | } 106 | 107 | def findNameByEmail(email: String): String= { 108 | play.api.db.DB.withConnection { implicit connection => 109 | SQL("select name from user where email = {email}").on( 110 | 'email -> email).as(SqlParser.scalar[String].single) 111 | } 112 | } 113 | 114 | 115 | def findByStatusByEmail(email: String): Int = { 116 | play.api.db.DB.withConnection { implicit connection => 117 | SQL("select status from user where email = {email}").on( 118 | 'email -> email).as(SqlParser.scalar[Int].single) 119 | } 120 | } 121 | 122 | 123 | def updateStatus(email:String): Int ={ 124 | play.api.db.DB.withConnection { implicit connection => 125 | SQL( 126 | """ 127 | update user set status=1 where email={email} 128 | """).on( 129 | 'email -> email 130 | ).executeUpdate() 131 | } 132 | } 133 | 134 | 135 | def updatePWD(email: String, password: String): Int={ 136 | play.api.db.DB.withConnection { implicit connection => 137 | SQL( 138 | """ 139 | update user set password={password} where 140 | email = {email} 141 | """).on( 142 | 'email -> email, 143 | 'password -> MD5Utils.encode2hex(password)).executeUpdate() 144 | } 145 | } 146 | 147 | /** 148 | * 149 | *Create a User. 150 | */ 151 | def create(user: User): User = { 152 | play.api.db.DB.withConnection { implicit connection => 153 | SQL( 154 | """ 155 | insert into user values ( 156 | {email}, {name}, {password},{status} 157 | ) 158 | """).on( 159 | 'email -> user.email, 160 | 'name -> user.name, 161 | 'password -> MD5Utils.encode2hex(user.password), 162 | 'status->user.status 163 | ).executeUpdate() 164 | user 165 | } 166 | } 167 | 168 | 169 | 170 | 171 | 172 | } 173 | 174 | 175 | -------------------------------------------------------------------------------- /app/models/user/package.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | /** 4 | * Created by kinge on 16/7/22. 5 | * The user related operations 6 | */ 7 | package object user { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /app/models/utils/Captcha.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import java.awt.geom.AffineTransform 4 | import java.awt._ 5 | import java.awt.image.BufferedImage 6 | import java.io._ 7 | import java.{lang, util} 8 | import javax.imageio.ImageIO 9 | 10 | import scala.util.Random 11 | 12 | /** 13 | * Created by kinge on 16/6/17. 14 | */ 15 | class CaptchaUtils(width:Int=160 ,height:Int=40 ,codeCount:Int=5 ,lineCount:Int=150) 16 | object CaptchaUtils{ 17 | 18 | val VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ" 19 | val random = new Random(); 20 | 21 | 22 | def generateVerifyCode(verifySize:Int):String={ 23 | generateVerifyCode(verifySize, VERIFY_CODES); 24 | } 25 | def generateVerifyCode(verifySize:Int, sources:String):String={ 26 | val codesLen = sources.length(); 27 | val rand = new Random(System.currentTimeMillis()); 28 | val verifyCode = new StringBuilder(verifySize); 29 | for(i<- 0 until verifySize){ 30 | verifyCode.append(sources.charAt(rand.nextInt(codesLen-1))) 31 | } 32 | return verifyCode.toString(); 33 | } 34 | 35 | 36 | def outputImage(w:Int, h:Int,code:String)={ 37 | val verifySize = code.length() 38 | val image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB) 39 | val rand = new Random() 40 | val g2 = image.createGraphics() 41 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON) 42 | val colors = new Array[Color](5) 43 | val colorSpaces = Array[Color](Color.WHITE, Color.CYAN, 44 | Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, 45 | Color.PINK, Color.YELLOW ) 46 | val fractions = new Array[Float](colors.length); 47 | for(i <- 0 until colors.length){ 48 | colors(i) = colorSpaces(rand.nextInt(colorSpaces.length)); 49 | fractions(i) = rand.nextFloat(); 50 | } 51 | util.Arrays.sort(fractions); 52 | 53 | g2.setColor(Color.GRAY); 54 | g2.fillRect(0, 0, w, h); 55 | 56 | val c = getRandColor(200, 250); 57 | g2.setColor(c); 58 | g2.fillRect(0, 2, w, h-4); 59 | 60 | 61 | val random = new Random(); 62 | g2.setColor(getRandColor(160, 200)); 63 | for (i <- 0 until 20) { 64 | val x = random.nextInt(w - 1); 65 | val y = random.nextInt(h - 1); 66 | val xl = random.nextInt(6) + 1; 67 | val yl = random.nextInt(12) + 1; 68 | g2.drawLine(x, y, x + xl + 40, y + yl + 20); 69 | } 70 | 71 | 72 | val yawpRate = 0.05f; 73 | val area = (yawpRate * w * h).asInstanceOf[Int]; 74 | for (j <- 0 until area) { 75 | val x = random.nextInt(w); 76 | val y = random.nextInt(h); 77 | val rgb = getRandomIntColor(); 78 | image.setRGB(x, y, rgb); 79 | } 80 | 81 | shear(g2, w, h, c); 82 | 83 | g2.setColor(getRandColor(100, 160)); 84 | val fontSize = h-4; 85 | val font = new Font("Algerian", Font.ITALIC, fontSize); 86 | g2.setFont(font); 87 | val chars = code.toCharArray(); 88 | for(i <- 0 until verifySize){ 89 | val affine = new AffineTransform(); 90 | affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (if (rand.nextBoolean()) 1 else -1), (w / verifySize) * i + fontSize/2, h/2); 91 | g2.setTransform(affine); 92 | g2.drawChars(chars, i, 1, ((w-10) / verifySize) * i + 5, h/2 + fontSize/2 - 10); 93 | } 94 | g2.dispose(); 95 | val baos = new ByteArrayOutputStream(); 96 | ImageIO.write(image, "jpg", baos); 97 | baos.flush() 98 | baos.toByteArray 99 | } 100 | 101 | def getRandColor(fcd:Int, bcd:Int):Color={ 102 | var fc=fcd 103 | var bc=bcd 104 | if (fc > 255) 105 | fc=255 106 | if (bc > 255) 107 | bc = 255; 108 | val r = fc + random.nextInt(bc - fc); 109 | val g = fc + random.nextInt(bc - fc); 110 | val b = fc + random.nextInt(bc - fc); 111 | new Color(r, g, b); 112 | } 113 | 114 | def getRandomIntColor():Int= { 115 | val rgb = getRandomRgb(); 116 | var color = 0; 117 | for (c <-rgb) { 118 | color = color << 8; 119 | color = color | c; 120 | } 121 | color; 122 | } 123 | 124 | def getRandomRgb() :Array[Int]={ 125 | val rgb = new Array[Int](3); 126 | for (i <- 0 to 2) { 127 | rgb(i) = random.nextInt(255); 128 | } 129 | return rgb; 130 | } 131 | 132 | def shear( g:Graphics, w1:Int, h1:Int, color:Color):Unit= { 133 | shearX(g, w1, h1, color); 134 | shearY(g, w1, h1, color); 135 | } 136 | 137 | def shearX(g:Graphics, w1:Int, h1:Int, color:Color):Unit={ 138 | val period = random.nextInt(2); 139 | val borderGap = true; 140 | val frames :Double= 1; 141 | val phase:Double = random.nextInt(2); 142 | 143 | for (i <- 0 until h1) { 144 | val d = (period >> 1).asInstanceOf[Double] * lang.Math.sin(i / period.asInstanceOf[Double] 145 | + (6.2831853071795862D * phase) / frames) 146 | g.copyArea(0, i, w1, 1, d.asInstanceOf[Int], 0) 147 | if (borderGap) { 148 | g.setColor(color); 149 | g.drawLine( d.asInstanceOf[Int], i, 0, i); 150 | g.drawLine(d.asInstanceOf[Int] + w1, i, w1, i); 151 | } 152 | } 153 | 154 | } 155 | 156 | def shearY( g:Graphics, w1:Int, h1:Int, color:Color)={ 157 | val period = random.nextInt(40) + 10; // 50; 158 | val borderGap = true; 159 | val frames:Double = 20; 160 | val phase:Double = 7; 161 | for (i <- 0 until w1) { 162 | val d = (period >> 1).asInstanceOf[Double] * lang.Math.sin(i / period.asInstanceOf[Double] 163 | + (6.2831853071795862D * phase) / frames) 164 | 165 | g.copyArea(i, 0, 1, h1, 0, d.asInstanceOf[Int]); 166 | if (borderGap) { 167 | g.setColor(color); 168 | g.drawLine(i, d.asInstanceOf[Int], i, 0); 169 | g.drawLine(i, d.asInstanceOf[Int] + h1, i, h1); 170 | } 171 | 172 | } 173 | } 174 | } 175 | 176 | -------------------------------------------------------------------------------- /app/models/utils/Config.scala: -------------------------------------------------------------------------------- 1 | package models.utils 2 | 3 | /** 4 | * Created by kinge on 16/5/2. 5 | */ 6 | trait Config { 7 | 8 | 9 | def getString(path:String):String 10 | 11 | def getBoolean(path:String):Boolean 12 | 13 | def getLong(path:String):Long 14 | 15 | def getInt(path:String):Int 16 | 17 | def getDouble(path:String):Double 18 | 19 | 20 | 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/models/utils/Configuration.scala: -------------------------------------------------------------------------------- 1 | package models.utils 2 | 3 | import com.typesafe.config.ConfigFactory 4 | 5 | /** 6 | * Created by kinge on 16/5/2. 7 | */ 8 | class Configuration extends Config{ 9 | 10 | val configName = "web-site" 11 | 12 | val config = ConfigFactory.load(configName) 13 | 14 | override def getString(path: String): String = config.getString(path) 15 | 16 | override def getDouble(path: String): Double = config.getDouble(path) 17 | 18 | override def getLong(path: String): Long = config.getLong(path) 19 | 20 | override def getBoolean(path: String): Boolean = config.getBoolean(path) 21 | 22 | override def getInt(path: String): Int = config.getInt(path) 23 | } 24 | -------------------------------------------------------------------------------- /app/models/utils/JsonParse.scala: -------------------------------------------------------------------------------- 1 | package models.utils 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import play.libs.Json 5 | 6 | import scala.collection.mutable.ArrayBuffer 7 | 8 | /** 9 | * Created by kinge on 16/8/24. 10 | */ 11 | object JsonParse { 12 | 13 | def jsonParse(jobQueueList :ArrayBuffer[Any]):String = { 14 | 15 | val mapper = new ObjectMapper() 16 | mapper.registerModule(com.fasterxml.jackson.module.scala.DefaultScalaModule) 17 | Json.setObjectMapper(mapper) 18 | 19 | val resultCSV = Json.toJson( 20 | Map[String, Any]( 21 | "jobs" -> jobQueueList 22 | ) 23 | ).toString 24 | resultCSV 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/models/utils/MD5Utils.scala: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import java.io.UnsupportedEncodingException 4 | import java.security.{MessageDigest, NoSuchAlgorithmException} 5 | 6 | object MD5Utils { 7 | 8 | private[this] def encode2bytes(source: String): Array[Byte] = { 9 | var result: Array[Byte] = null 10 | try { 11 | val md = MessageDigest.getInstance("MD5"); 12 | md.reset(); 13 | md.update(source.getBytes("UTF-8")); 14 | result = md.digest() 15 | } catch { 16 | case e: NoSuchAlgorithmException => e.printStackTrace() 17 | case e: UnsupportedEncodingException => e.printStackTrace() 18 | } 19 | result; 20 | } 21 | 22 | def encode2hex(source: String): String = { 23 | val data = encode2bytes(source); 24 | val hexString = new StringBuffer(); 25 | for (byte <- data) { 26 | val hex: String = Integer.toHexString(0xff & byte); 27 | if (hex.length() == 1) { 28 | hexString.append('0'); 29 | } 30 | hexString.append(hex); 31 | } 32 | hexString.toString 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/views/copyright.scala.html: -------------------------------------------------------------------------------- 1 |

6 | -------------------------------------------------------------------------------- /app/views/error.scala.html: -------------------------------------------------------------------------------- 1 | @(mess:String) 2 | 3 | @mess 4 | -------------------------------------------------------------------------------- /app/views/errors/fzf.scala.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Page not found : 404 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |

404

19 |

Oops! Bad request ...

20 |

Request not found!

21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |
32 | 33 |
34 | 35 |
36 |
37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/views/findpwd.scala.html: -------------------------------------------------------------------------------- 1 | @(form: Form[(String, String)]) 2 | 3 | 4 | @plain("Password back") { 5 |

Password back

6 |
7 | 8 | 13 | 14 | @helper.form(routes.Authentication.resetpwd) { 15 | 16 | @if(form.hasErrors && !form.error("email").isEmpty) { 17 |

@form.error("email").get.message

18 | } 19 | 20 |
21 | 22 |
23 | 24 | 25 |
26 | @if(form.hasErrors && !form.error("captcha").isEmpty) { 27 |

@form.error("captcha").get.message

28 | } 29 | 30 |
31 | 37 | } 38 |
39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /app/views/login.scala.html: -------------------------------------------------------------------------------- 1 | @(form: Form[(String, String)])(implicit flash: Flash) 2 | 3 | @plain("Neptune Login") { 4 |

Welcome to Spark-submit-ui

5 |
6 | 7 | @helper.form(routes.Authentication.authenticate) { 8 | @form.globalError.map { error => 9 |

10 | Wrong username or password 11 |

12 | } 13 | 14 | @if(form.hasErrors && !form.error("email").isEmpty) { 15 |

@form.error("email").get.message

16 | } 17 | 18 | @flash.get("success").map { message => 19 |

20 | @message 21 |

22 | } 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 40 | } 41 |
42 | 43 | } 44 | -------------------------------------------------------------------------------- /app/views/nothing.scala.html: -------------------------------------------------------------------------------- 1 | @(args:String) 2 | 3 | @args 4 | -------------------------------------------------------------------------------- /app/views/plain.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String)(content: Html) 2 | 3 | 4 | 5 | 6 | @title 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | @content 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/views/registed.scala.html: -------------------------------------------------------------------------------- 1 | @(result: Option[String]) 2 | 3 | 4 | 5 | 6 | 7 | Registration results 8 | 9 | 10 | @if(!result.isEmpty) { 11 |

@result.get

12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/views/register.scala.html: -------------------------------------------------------------------------------- 1 | @(form: Form[Registration]) 2 | 3 | @plain("register"){ 4 |

Welcome to register

5 |
6 | 7 | @helper.form(routes.Authentication.verifying){ 8 | 9 | @if(form.hasErrors && !form.error("email").isEmpty){ 10 | @if(form.error("email").get.message.equals("error.required")){ 11 | Email cannot be empty 12 | }else{ 13 |

@form.error("email").get.message

14 | } 15 | } 16 | 17 | @if(!form.error("name").isEmpty){ 18 | @if(form.error("name").get.message.equals("error.required")){ 19 | The name cannot be empty 20 | }else{ 21 |

@form.error("name").get.message

22 | } 23 | } 24 | 25 | 26 | @if(form.hasErrors && !form.error("repassword").isEmpty){ 27 | @if(form.error("repassword").get.message.equals("error.required")){ 28 | Password cannot be empty 29 | }else{ 30 |

@form.error("repassword").get.message

31 | } 32 | } 33 | 34 | @if(form.hasErrors){ 35 | @form.globalError.map { error => 36 |

37 | @error.message 38 |

39 | } 40 | } 41 |
42 | 43 |
44 | 45 | 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/views/setpwd.scala.html: -------------------------------------------------------------------------------- 1 | @(form: Form[(String,String)]) 2 | 3 | @plain("Set the password"){ 4 |

Set the password

5 |
6 | 7 | @if(form.hasErrors){ 8 | @form.globalError.map { error => 9 |

10 | @error.message 11 |

12 | } 13 | } 14 | 15 | @helper.form(routes.Authentication.updatepwd){ 16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 | 24 | 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/views/sparklist.scala.html: -------------------------------------------------------------------------------- 1 | @sidebar = { 2 | 76 | } 77 | @main("Spark-Apps")(sidebar) { 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 |
93 | 94 |
95 |

Spark apps information list

96 |

Spark apps for all users

97 |
98 | 99 | 115 | 116 |
117 | 118 | 119 |
120 | 121 |
122 |
123 |

Spark-Apps

124 | 125 | 131 |
132 |
133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 |
APP IDNameDurationStateMemoryperslaveStarttime
149 | 150 |
151 |
152 | @copyright() 153 | 154 | 155 | 156 |
157 | 158 | 159 | 160 | } 161 | -------------------------------------------------------------------------------- /app/views/yarnlist.scala.html: -------------------------------------------------------------------------------- 1 | @sidebar = { 2 | 74 | } 75 | @main("Yarn-Apps")(sidebar) { 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 |
91 | 92 |
93 |

Yarn apps information list

94 |

yarn apps for all users

95 |
96 | 97 | 112 | 113 |
114 | 115 | 116 |
117 | 118 |
119 |
120 |

Yarn-Apps

121 | 122 | 128 |
129 |
130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 |
JobIDUserQueueStateFinalStatusProgressStartedTimeFinishedTime
147 | 148 |
149 |
150 | @copyright() 151 | 152 | 153 | 154 |
155 | 156 | 157 | 158 | } 159 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | name := "spark-submit-ui" 2 | 3 | version := "v1.01 4 | 5 | scalaVersion := "2.11.8" 6 | 7 | libraryDependencies ++= Seq( 8 | jdbc, 9 | anorm, 10 | cache, 11 | "org.apache.commons" % "commons-email" % "1.4", 12 | "org.apache.spark" % "spark-core_2.10" % "1.4.1", 13 | "com.google.inject" % "guice" % "3.0", 14 | "com.tzavellas" % "sse-guice" % "0.7.1", 15 | "com.jolbox" % "bonecp" % "0.8.0.RELEASE" 16 | ) 17 | 18 | play.Project.playScalaSettings 19 | 20 | resolvers ++= Seq( 21 | "Apache Repository" at "https://repository.apache.org/content/repositories/releases/", 22 | "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases", 23 | Resolver.sonatypeRepo("public") 24 | ) 25 | 26 | ivyScala := ivyScala.value map { _.copy(overrideScalaVersion = true) } 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /conf/application.conf: -------------------------------------------------------------------------------- 1 | # This is the main configuration file for the application. 2 | # ~~~~~ 3 | 4 | # Secret key 5 | # ~~~~~ 6 | # The secret key is used to secure cryptographics functions. 7 | # If you deploy your application to several instances be sure to use the same key! 8 | application.secret="VD]<6qacNmRhB1c[pQxZ6cS5rf[JrVi2wJgj?a@iWyyg" 9 | 10 | # The application languages 11 | # ~~~~~ 12 | application.langs="en" 13 | 14 | # Global object class 15 | # ~~~~~ 16 | # Define the Global object class for this application. 17 | # Default to Global in the root package. 18 | # application.global=Global 19 | 20 | # Router 21 | # ~~~~~ 22 | # Define the Router object to use for this application. 23 | # This router will be looked up first when the application is starting up, 24 | # so make sure this is the entry point. 25 | # Furthermore, it's assumed your route file is named properly. 26 | # So for an application router like `my.application.Router`, 27 | # you may need to define a router file `conf/my.application.routes`. 28 | # Default to Routes in the root package (and conf/routes) 29 | # application.router=my.application.Routes 30 | 31 | # Database configuration 32 | # ~~~~~ 33 | # You can declare as many datasources as you want. 34 | # By convention, the default datasource is named `default` 35 | # 36 | # db.default.driver=org.h2.Driver 37 | # db.default.url="jdbc:h2:mem:play" 38 | # db.default.user=sa 39 | # db.default.password="" 40 | 41 | db.default.driver=org.h2.Driver 42 | db.default.url="jdbc:h2:db/play;FILE_LOCK=NO" 43 | 44 | 45 | 46 | 47 | # Ebean configuration 48 | #ebean.default="models.*" 49 | 50 | # Evolutions 51 | # ~~~~~ 52 | # You can disable evolutions if needed 53 | # evolutionplugin=disabled 54 | 55 | # Logger 56 | # ~~~~~ 57 | # You can also configure logback (http://logback.qos.ch/), 58 | # by providing an application-logger.xml file in the conf directory. 59 | 60 | # Root logger: 61 | logger.root=ERROR 62 | 63 | # Logger used by the framework: 64 | logger.play=INFO 65 | 66 | # Logger provided to your application: 67 | logger.application=DEBUG 68 | 69 | akka.default-dispatcher.fork-join-executor.pool-size-max =64 70 | akka.actor.debug.receive = on 71 | 72 | 73 | #Database connection pool configuration 74 | # Set a connection's default isolation level 75 | db.default.isolation=READ_COMMITTED 76 | 77 | # In order to reduce lock contention and thus improve performance, 78 | # each incoming connection request picks off a connection from a 79 | # pool that has thread-affinity. 80 | # The higher this number, the better your performance will be for the 81 | # case when you have plenty of short-lived threads. 82 | # Beyond a certain threshold, maintenance of these pools will start 83 | # to have a negative effect on performance (and only for the case 84 | # when connections on a partition start running out). 85 | db.default.partitionCount=2 86 | 87 | # The number of connections to create per partition. Setting this to 88 | # 5 with 3 partitions means you will have 15 unique connections to the 89 | # database. Note that BoneCP will not create all these connections in 90 | # one go but rather start off with minConnectionsPerPartition and 91 | # gradually increase connections as required. 92 | db.default.maxConnectionsPerPartition=100 93 | 94 | # The number of initial connections, per partition. 95 | db.default.minConnectionsPerPartition=10 96 | 97 | # When the available connections are about to run out, BoneCP will 98 | # dynamically create new ones in batches. This property controls 99 | # how many new connections to create in one go (up to a maximum of 100 | # maxConnectionsPerPartition). Note: This is a per-partition setting. 101 | db.default.acquireIncrement=10 102 | 103 | # After attempting to acquire a connection and failing, try to 104 | # connect this number of times before giving up. 105 | db.default.acquireRetryAttempts=10 106 | 107 | # How long to wait before attempting to obtain a 108 | # connection again after a failure. 109 | db.default.acquireRetryDelay=10 minute 110 | 111 | # The maximum time to wait before a call 112 | # to getConnection is timed out. 113 | db.default.connectionTimeout=10 second 114 | 115 | # Idle max age 116 | db.default.idleMaxAge=10 minute 117 | 118 | # This sets the time for a connection to remain idle before sending a test query to the DB. 119 | # This is useful to prevent a DB from timing out connections on its end. 120 | db.default.idleConnectionTestPeriod=5 minutes 121 | 122 | # An initial SQL statement that is run only when 123 | # a connection is first created. 124 | #db.default.initSQL="SELECT 1" 125 | 126 | # If enabled, log SQL statements being executed. 127 | db.default.logStatements=false 128 | 129 | # The maximum connection age. 130 | db.default.maxConnectionAge=10 minute 131 | -------------------------------------------------------------------------------- /conf/evolutions/default/1.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 5.7.12, for osx10.9 (x86_64) 2 | -- 3 | -- Host: 10.73.33.41 Database: playdb 4 | -- ------------------------------------------------------ 5 | -- Server version 5.6.30-76.3 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `user` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `user`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!40101 SET character_set_client = utf8 */; 25 | CREATE TABLE `user` ( 26 | `email` varchar(255) NOT NULL, 27 | `name` varchar(255) NOT NULL, 28 | `password` varchar(255) NOT NULL, 29 | `status` int(4) NOT NULL DEFAULT '0' 30 | PRIMARY KEY (`email`) 31 | ) 32 | /*!40101 SET character_set_client = @saved_cs_client */; 33 | 34 | 35 | -------------------------------------------------------------------------------- /conf/evolutions/default/2.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 5.7.12, for osx10.9 (x86_64) 2 | -- 3 | -- Host: 10.73.33.41 Database: playdb 4 | -- ------------------------------------------------------ 5 | -- Server version 5.6.30-76.3 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `hadoop_metrics` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `hadoop_metrics`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!40101 SET character_set_client = utf8 */; 25 | CREATE TABLE `hadoop_metrics` ( 26 | `ReceivedBytes` bigint(255) NOT NULL, 27 | `SentBytes` bigint(255) NOT NULL, 28 | `host` varchar(45) NOT NULL, 29 | `CapacityUsed` bigint(225) NOT NULL, 30 | `CapacityRemaining` bigint(255) NOT NULL, 31 | `CapacityUsedNonDFS` bigint(255) NOT NULL, 32 | `MemHeapUsedM` double NOT NULL, 33 | `MemNonHeapUsedM` double NOT NULL, 34 | `timestamp` bigint(255) NOT NULL 35 | ) 36 | /*!40101 SET character_set_client = @saved_cs_client */; 37 | 38 | 39 | -------------------------------------------------------------------------------- /conf/evolutions/default/3.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 5.7.12, for osx10.9 (x86_64) 2 | -- 3 | -- Host: 10.73.33.41 Database: playdb 4 | -- ------------------------------------------------------ 5 | -- Server version 5.6.30-76.3 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `task_msg` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `task_msg`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!40101 SET character_set_client = utf8 */; 25 | CREATE TABLE `task_msg` ( 26 | `id` varchar(255) NOT NULL, 27 | `state` varchar(45) DEFAULT NULL, 28 | `user` varchar(255) DEFAULT NULL, 29 | PRIMARY KEY (`id`) 30 | ) 31 | /*!40101 SET character_set_client = @saved_cs_client */; 32 | 33 | -------------------------------------------------------------------------------- /conf/evolutions/default/4.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 5.7.12, for osx10.9 (x86_64) 2 | -- 3 | -- Host: 10.73.33.41 Database: playdb 4 | -- ------------------------------------------------------ 5 | -- Server version 5.6.30-76.3 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `task_standalone` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `task_standalone`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!40101 SET character_set_client = utf8 */; 25 | CREATE TABLE `task_standalone` ( 26 | `app_id` varchar(255) NOT NULL, 27 | `name` varchar(45) DEFAULT NULL, 28 | `cores` int(11) DEFAULT NULL, 29 | `memoryperslave` int(10) DEFAULT NULL, 30 | `state` varchar(45) DEFAULT NULL, 31 | `starttime` bigint(255) DEFAULT NULL, 32 | `duration` bigint(255) DEFAULT NULL, 33 | `user` varchar(45) DEFAULT NULL, 34 | PRIMARY KEY (`app_id`) 35 | ) 36 | /*!40101 SET character_set_client = @saved_cs_client */; 37 | 38 | 39 | -------------------------------------------------------------------------------- /conf/evolutions/default/5.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 5.7.12, for osx10.9 (x86_64) 2 | -- 3 | -- Host: 10.73.33.41 Database: playdb 4 | -- ------------------------------------------------------ 5 | -- Server version 5.6.30-76.3 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `task_yarn` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `task_yarn`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!40101 SET character_set_client = utf8 */; 25 | CREATE TABLE `task_yarn` ( 26 | `application_id` varchar(255) NOT NULL, 27 | `name` varchar(45) DEFAULT NULL, 28 | `apptype` varchar(45) DEFAULT NULL, 29 | `queue` varchar(45) DEFAULT NULL, 30 | `starttime` bigint(255) DEFAULT NULL, 31 | `finishtime` bigint(255) DEFAULT NULL, 32 | `state` varchar(45) DEFAULT NULL, 33 | `user` varchar(45) DEFAULT NULL, 34 | PRIMARY KEY (`application_id`) 35 | ) 36 | /*!40101 SET character_set_client = @saved_cs_client */; 37 | 38 | -------------------------------------------------------------------------------- /conf/evolutions/default/6.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 5.7.12, for osx10.9 (x86_64) 2 | -- 3 | -- Host: 10.73.33.41 Database: playdb 4 | -- ------------------------------------------------------ 5 | -- Server version 5.6.30-76.3 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `task_args` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `task_args`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!40101 SET character_set_client = utf8 */; 25 | CREATE TABLE `task_args` ( 26 | `id` varchar(100) NOT NULL, 27 | `master` varchar(45) DEFAULT NULL, 28 | `executeClass` varchar(45) DEFAULT NULL, 29 | `numExecutors` varchar(45) DEFAULT NULL, 30 | `driverMemory` varchar(45) DEFAULT NULL, 31 | `executorMemory` varchar(45) DEFAULT NULL, 32 | `total_executor_cores` varchar(45) DEFAULT NULL, 33 | `jarLocation` varchar(500) DEFAULT NULL, 34 | `args1` varchar(45) DEFAULT NULL, 35 | PRIMARY KEY (`id`) 36 | ) 37 | /*!40101 SET character_set_client = @saved_cs_client */; 38 | 39 | -------------------------------------------------------------------------------- /conf/routes: -------------------------------------------------------------------------------- 1 | # Routes 2 | # This file defines all application routes (Higher priority routes first) 3 | # ~~~~ 4 | 5 | # Home page 6 | GET / @controllers.Application.index 7 | GET /login controllers.Authentication.login 8 | POST /login controllers.Authentication.authenticate 9 | GET /logout controllers.Authentication.logout 10 | 11 | #Dashboard 12 | 13 | GET /spark_info @controllers.SparkList.getSparkInfo 14 | GET /yarn_info @controllers.YarnList.getYarnInfo 15 | GET /yarnlist @controllers.YarnList.yarnlist 16 | GET /sparklist @controllers.SparkList.sparklist 17 | GET /spark_apps @controllers.SparkList.getSaprkApps 18 | 19 | 20 | # Map static resources from the /public folder to the /assets URL path 21 | GET /assets/*file controllers.Assets.at(path="/public", file) 22 | 23 | #User 24 | GET /registration controllers.Authentication.registration 25 | POST /registration controllers.Authentication.verifying 26 | GET /mail controllers.Authentication.mail(user) 27 | GET /mail/verifyingmail controllers.Authentication.verifyingmail(email,validateCode) 28 | GET /findpwd controllers.Authentication.findpwd 29 | GET /captcha controllers.Authentication.captcha 30 | POST /findpwd controllers.Authentication.resetpwd 31 | GET /mail/setpwd controllers.Authentication.setpwd(email,pwdToken) 32 | POST /updatepwd controllers.Authentication.updatepwd 33 | 34 | 35 | 36 | # Spark Jar 37 | POST /sparkjar @controllers.SparkJar.upload 38 | GET /executejar @controllers.SparkJar.executejarpage 39 | POST /executejar @controllers.SparkJar.executejar 40 | GET /errorlog @controllers.SparkJar.errorpage(error:String) 41 | 42 | 43 | 44 | 45 | #return User 46 | GET /whoiam controllers.UserName.userName 47 | 48 | #Job 49 | GET /tasklist @controllers.TaskManager.tasklist 50 | GET /standalone @controllers.TaskManager.standaloneInfo 51 | GET /yarn @controllers.TaskManager.yarnInfo 52 | GET /kill @controllers.TaskManager.kill(appId:String) 53 | GET /rerun @controllers.TaskManager.rerun(appId:String) 54 | 55 | 56 | #PUSH 57 | GET /startpush @controllers.Application.startpush 58 | GET /msglist @controllers.Application.msglist 59 | GET /read @controllers.Application.read(appId:String) 60 | GET /readall @controllers.Application.readall 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /conf/web-site.conf: -------------------------------------------------------------------------------- 1 | #spark home 2 | spark.home="/usr/local/spark/" 3 | 4 | #spark master url 5 | spark.master.url ="localhost:7077" 6 | 7 | #spark rest api url 8 | spark.master.host="localhost:8080" 9 | 10 | 11 | #yarn rest api url 12 | hadoop.yarn.host="localhost:8088" 13 | 14 | 15 | hadoop.metrics.host ="localhost" 16 | 17 | # Monitoring data update interval 18 | #ms 19 | #metrics.data-update.interval-ms = 300000 20 | metrics.data-update.interval-ms = 60000 21 | 22 | #Receive mail address, the deployment of the host address * 23 | email.default.host="localhost:9000" 24 | 25 | 26 | 27 | ########## Task run configuration ########## 28 | 29 | #The task status update interval 30 | #ms 31 | task.data-update.interval-ms = 5000 32 | 33 | #Start threads 34 | task.pool.max = 20 35 | 36 | # Jar file path 37 | job.upload.path="/tmp/file/" 38 | 39 | # File upload timeout 40 | # seconds 41 | job.upload.timeout.seconds= 100 42 | 43 | # Task submitted timeout 44 | #seconds 45 | job.submit.timeout.seconds = 300 46 | 47 | #The initialization tasks parameter timeout 48 | #seconds 49 | job.request.timeout.seconds = 50 50 | 51 | ##### Email configuration ######## 52 | email.server.host="" 53 | email.default.username="" 54 | email.default.password="" 55 | email.default.port= 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /db/play.h2.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/db/play.h2.db -------------------------------------------------------------------------------- /db/play.h2.db.trace.db: -------------------------------------------------------------------------------- 1 | 03-07 12:10:45 jdbc[4]: java.lang.Exception: Open Stack Trace 2 | at org.h2.util.CloseWatcher.register(CloseWatcher.java:99) 3 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:117) 4 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:90) 5 | at org.h2.Driver.connect(Driver.java:73) 6 | at org.h2.server.web.WebServer.getConnection(WebServer.java:655) 7 | at org.h2.server.web.WebApp.login(WebApp.java:900) 8 | at org.h2.server.web.WebApp.process(WebApp.java:205) 9 | at org.h2.server.web.WebApp.processRequest(WebApp.java:164) 10 | at org.h2.server.web.WebThread.process(WebThread.java:138) 11 | at org.h2.server.web.WebThread.run(WebThread.java:94) 12 | at java.lang.Thread.run(Thread.java:744) 13 | 14 | org.h2.message.DbException: The connection was not closed by the application and is garbage collected [90018-172] 15 | at org.h2.message.DbException.get(DbException.java:169) 16 | at org.h2.message.DbException.get(DbException.java:146) 17 | at org.h2.message.DbException.get(DbException.java:135) 18 | at org.h2.jdbc.JdbcConnection.closeOld(JdbcConnection.java:171) 19 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:116) 20 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:90) 21 | at org.h2.Driver.connect(Driver.java:73) 22 | at org.h2.server.web.WebServer.getConnection(WebServer.java:655) 23 | at org.h2.server.web.WebApp.test(WebApp.java:840) 24 | at org.h2.server.web.WebApp.process(WebApp.java:215) 25 | at org.h2.server.web.WebApp.processRequest(WebApp.java:164) 26 | at org.h2.server.web.WebThread.process(WebThread.java:138) 27 | at org.h2.server.web.WebThread.run(WebThread.java:94) 28 | at java.lang.Thread.run(Thread.java:744) 29 | Caused by: org.h2.jdbc.JdbcSQLException: The connection was not closed by the application and is garbage collected [90018-172] 30 | at org.h2.message.DbException.getJdbcSQLException(DbException.java:329) 31 | ... 14 more 32 | 03-07 12:22:19 jdbc[7]: java.lang.Exception: Open Stack Trace 33 | at org.h2.util.CloseWatcher.register(CloseWatcher.java:99) 34 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:117) 35 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:90) 36 | at org.h2.Driver.connect(Driver.java:73) 37 | at org.h2.server.web.WebServer.getConnection(WebServer.java:655) 38 | at org.h2.server.web.WebApp.login(WebApp.java:900) 39 | at org.h2.server.web.WebApp.process(WebApp.java:205) 40 | at org.h2.server.web.WebApp.processRequest(WebApp.java:164) 41 | at org.h2.server.web.WebThread.process(WebThread.java:138) 42 | at org.h2.server.web.WebThread.run(WebThread.java:94) 43 | at java.lang.Thread.run(Thread.java:744) 44 | 45 | org.h2.message.DbException: The connection was not closed by the application and is garbage collected [90018-172] 46 | at org.h2.message.DbException.get(DbException.java:169) 47 | at org.h2.message.DbException.get(DbException.java:146) 48 | at org.h2.message.DbException.get(DbException.java:135) 49 | at org.h2.jdbc.JdbcConnection.closeOld(JdbcConnection.java:171) 50 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:116) 51 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:90) 52 | at org.h2.Driver.connect(Driver.java:73) 53 | at org.h2.server.web.WebServer.getConnection(WebServer.java:655) 54 | at org.h2.server.web.WebApp.test(WebApp.java:840) 55 | at org.h2.server.web.WebApp.process(WebApp.java:215) 56 | at org.h2.server.web.WebApp.processRequest(WebApp.java:164) 57 | at org.h2.server.web.WebThread.process(WebThread.java:138) 58 | at org.h2.server.web.WebThread.run(WebThread.java:94) 59 | at java.lang.Thread.run(Thread.java:744) 60 | Caused by: org.h2.jdbc.JdbcSQLException: The connection was not closed by the application and is garbage collected [90018-172] 61 | at org.h2.message.DbException.getJdbcSQLException(DbException.java:329) 62 | ... 14 more 63 | 03-07 12:22:19 jdbc[7]: java.lang.Exception: Open Stack Trace 64 | at org.h2.util.CloseWatcher.register(CloseWatcher.java:99) 65 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:117) 66 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:90) 67 | at org.h2.Driver.connect(Driver.java:73) 68 | at org.h2.server.web.WebServer.getConnection(WebServer.java:655) 69 | at org.h2.server.web.WebApp.login(WebApp.java:900) 70 | at org.h2.server.web.WebApp.process(WebApp.java:205) 71 | at org.h2.server.web.WebApp.processRequest(WebApp.java:164) 72 | at org.h2.server.web.WebThread.process(WebThread.java:138) 73 | at org.h2.server.web.WebThread.run(WebThread.java:94) 74 | at java.lang.Thread.run(Thread.java:744) 75 | 76 | org.h2.message.DbException: The connection was not closed by the application and is garbage collected [90018-172] 77 | at org.h2.message.DbException.get(DbException.java:169) 78 | at org.h2.message.DbException.get(DbException.java:146) 79 | at org.h2.message.DbException.get(DbException.java:135) 80 | at org.h2.jdbc.JdbcConnection.closeOld(JdbcConnection.java:171) 81 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:116) 82 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:90) 83 | at org.h2.Driver.connect(Driver.java:73) 84 | at org.h2.server.web.WebServer.getConnection(WebServer.java:655) 85 | at org.h2.server.web.WebApp.test(WebApp.java:840) 86 | at org.h2.server.web.WebApp.process(WebApp.java:215) 87 | at org.h2.server.web.WebApp.processRequest(WebApp.java:164) 88 | at org.h2.server.web.WebThread.process(WebThread.java:138) 89 | at org.h2.server.web.WebThread.run(WebThread.java:94) 90 | at java.lang.Thread.run(Thread.java:744) 91 | Caused by: org.h2.jdbc.JdbcSQLException: The connection was not closed by the application and is garbage collected [90018-172] 92 | at org.h2.message.DbException.getJdbcSQLException(DbException.java:329) 93 | ... 14 more 94 | -------------------------------------------------------------------------------- /project/.sbtserver: -------------------------------------------------------------------------------- 1 | #Server Startup at 2017-03-06T09:15+0000 2 | #Mon Mar 06 17:15:49 HKT 2017 3 | server.uri=http\://0.0.0.0\:49360 4 | -------------------------------------------------------------------------------- /project/.sbtserver.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/project/.sbtserver.lock -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=0.13.11 2 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | // Comment to get more information during initialization 2 | logLevel := Level.Warn 3 | 4 | // The Typesafe repository 5 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/" 6 | 7 | // Use the Play sbt plugin for Play projects 8 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.2.6") -------------------------------------------------------------------------------- /project/sbt-ui.sbt: -------------------------------------------------------------------------------- 1 | // This plugin represents functionality that is to be added to sbt in the future 2 | 3 | addSbtPlugin("org.scala-sbt" % "sbt-core-next" % "0.1.1") -------------------------------------------------------------------------------- /public/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /public/fonts/linecons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/linecons.eot -------------------------------------------------------------------------------- /public/fonts/linecons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/linecons.ttf -------------------------------------------------------------------------------- /public/fonts/linecons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/linecons.woff -------------------------------------------------------------------------------- /public/images/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/1.png -------------------------------------------------------------------------------- /public/images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/2.png -------------------------------------------------------------------------------- /public/images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/3.png -------------------------------------------------------------------------------- /public/images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/4.png -------------------------------------------------------------------------------- /public/images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/5.png -------------------------------------------------------------------------------- /public/images/522641-46907f6c8f3b2b0b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/522641-46907f6c8f3b2b0b.png -------------------------------------------------------------------------------- /public/images/QQ20170313-162151@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/QQ20170313-162151@2x.png -------------------------------------------------------------------------------- /public/images/QQ20170313-171517@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/QQ20170313-171517@2x.png -------------------------------------------------------------------------------- /public/images/album-image-full.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-image-full.jpg -------------------------------------------------------------------------------- /public/images/album-img-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-1.png -------------------------------------------------------------------------------- /public/images/album-img-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-2.png -------------------------------------------------------------------------------- /public/images/album-img-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-3.png -------------------------------------------------------------------------------- /public/images/album-img-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-4.png -------------------------------------------------------------------------------- /public/images/album-img-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-5.png -------------------------------------------------------------------------------- /public/images/album-img-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-6.png -------------------------------------------------------------------------------- /public/images/album-img-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-7.png -------------------------------------------------------------------------------- /public/images/album-img-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-8.png -------------------------------------------------------------------------------- /public/images/arrow-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/arrow-left.png -------------------------------------------------------------------------------- /public/images/arrow-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/arrow-right.png -------------------------------------------------------------------------------- /public/images/attach-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/attach-1.png -------------------------------------------------------------------------------- /public/images/attach-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/attach-2.png -------------------------------------------------------------------------------- /public/images/back_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/back_disabled.png -------------------------------------------------------------------------------- /public/images/back_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/back_enabled.png -------------------------------------------------------------------------------- /public/images/back_enabled_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/back_enabled_hover.png -------------------------------------------------------------------------------- /public/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/bg.jpg -------------------------------------------------------------------------------- /public/images/chosen-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/chosen-sprite.png -------------------------------------------------------------------------------- /public/images/chosen-sprite@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/chosen-sprite@2x.png -------------------------------------------------------------------------------- /public/images/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/cloud.png -------------------------------------------------------------------------------- /public/images/clouds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/clouds.png -------------------------------------------------------------------------------- /public/images/eiffel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/eiffel.jpg -------------------------------------------------------------------------------- /public/images/forward_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/forward_disabled.png -------------------------------------------------------------------------------- /public/images/forward_enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/forward_enabled.png -------------------------------------------------------------------------------- /public/images/forward_enabled_hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/forward_enabled_hover.png -------------------------------------------------------------------------------- /public/images/image-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/image-1.jpg -------------------------------------------------------------------------------- /public/images/image-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/image-2.jpg -------------------------------------------------------------------------------- /public/images/image-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/image-3.jpg -------------------------------------------------------------------------------- /public/images/image-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/image-4.jpg -------------------------------------------------------------------------------- /public/images/image-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/image-5.jpg -------------------------------------------------------------------------------- /public/images/logo-collapsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/logo-collapsed.png -------------------------------------------------------------------------------- /public/images/logo-collapsed2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/logo-collapsed2x.png -------------------------------------------------------------------------------- /public/images/logo-white-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/logo-white-bg.png -------------------------------------------------------------------------------- /public/images/logo-white-bg@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/logo-white-bg@2x.png -------------------------------------------------------------------------------- /public/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/logo.png -------------------------------------------------------------------------------- /public/images/logo2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/logo2x.png -------------------------------------------------------------------------------- /public/images/minus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/minus.png -------------------------------------------------------------------------------- /public/images/news-image-widget-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/news-image-widget-2.png -------------------------------------------------------------------------------- /public/images/news-image-widget-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/news-image-widget-3.png -------------------------------------------------------------------------------- /public/images/news-image-widget-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/news-image-widget-4.png -------------------------------------------------------------------------------- /public/images/news-image-widget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/news-image-widget.png -------------------------------------------------------------------------------- /public/images/notes-lines.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/notes-lines.png -------------------------------------------------------------------------------- /public/images/ok-white-full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/ok-white-full.png -------------------------------------------------------------------------------- /public/images/ok-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/ok-white.png -------------------------------------------------------------------------------- /public/images/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/ok.png -------------------------------------------------------------------------------- /public/images/pic1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/pic1.png -------------------------------------------------------------------------------- /public/images/pic2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/pic2.png -------------------------------------------------------------------------------- /public/images/pic3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/pic3.jpg -------------------------------------------------------------------------------- /public/images/pic4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/pic4.jpg -------------------------------------------------------------------------------- /public/images/plus-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/plus-white.png -------------------------------------------------------------------------------- /public/images/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/plus.png -------------------------------------------------------------------------------- /public/images/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/sample.jpg -------------------------------------------------------------------------------- /public/images/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/sort_asc.png -------------------------------------------------------------------------------- /public/images/sort_asc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/sort_asc_disabled.png -------------------------------------------------------------------------------- /public/images/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/sort_both.png -------------------------------------------------------------------------------- /public/images/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/sort_desc.png -------------------------------------------------------------------------------- /public/images/sort_desc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/sort_desc_disabled.png -------------------------------------------------------------------------------- /public/images/spritemap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/spritemap.png -------------------------------------------------------------------------------- /public/images/spritemap@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/spritemap@2x.png -------------------------------------------------------------------------------- /public/images/switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/switch.png -------------------------------------------------------------------------------- /public/images/user-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/user-1.png -------------------------------------------------------------------------------- /public/images/user-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/user-2.png -------------------------------------------------------------------------------- /public/images/user-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/user-3.png -------------------------------------------------------------------------------- /public/images/user-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/user-4.png -------------------------------------------------------------------------------- /public/images/user-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/user-5.png -------------------------------------------------------------------------------- /public/javascripts/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/javascripts/.DS_Store -------------------------------------------------------------------------------- /public/javascripts/globalize.min.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/javascripts/globalize.min.js -------------------------------------------------------------------------------- /public/javascripts/highchartThemes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jhazheng on 16/7/25. 3 | */ 4 | 5 | Highcharts.createElement('link', { 6 | // href: 'http://fonts.googleapis.com/css?family=Unica+One', 7 | rel: 'stylesheet', 8 | type: 'text/css' 9 | }, null, document.getElementsByTagName('head')[0]); 10 | 11 | Highcharts.theme = { 12 | colors: ["#2b908f", "#90ee7e", "#f45b5b", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee", 13 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], 14 | chart: { 15 | backgroundColor: { 16 | linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 }, 17 | stops: [ 18 | [0, '#FFFFFF'], 19 | [1, '#FFFFFF'] 20 | ] 21 | }, 22 | style: { 23 | fontFamily: "'Unica One', sans-serif" 24 | }, 25 | plotBorderColor: '#606063' 26 | }, 27 | title: { 28 | style: { 29 | color: '#050505', 30 | textTransform: 'uppercase', 31 | fontSize: '20px' 32 | } 33 | }, 34 | subtitle: { 35 | style: { 36 | color: '#050505', 37 | textTransform: 'uppercase' 38 | } 39 | }, 40 | xAxis: { 41 | gridLineColor: '#43CD80', 42 | labels: { 43 | style: { 44 | color: '#050505' 45 | } 46 | }, 47 | lineColor: '#43CD80', 48 | minorGridLineColor: '#505053', 49 | tickColor: '#43CD80', 50 | title: { 51 | style: { 52 | color: '#A0A0A3' 53 | 54 | } 55 | } 56 | }, 57 | yAxis: { 58 | gridLineColor: '#43CD80', 59 | labels: { 60 | style: { 61 | color: '#050505' 62 | } 63 | }, 64 | lineColor: '#43CD80', 65 | minorGridLineColor: '#505053', 66 | tickColor: '#43CD80', 67 | tickWidth: 1, 68 | title: { 69 | style: { 70 | color: '#A0A0A3' 71 | } 72 | } 73 | }, 74 | tooltip: { 75 | backgroundColor: 'rgba(0, 0, 0, 0.85)', 76 | style: { 77 | color: '#F0F0F0' 78 | } 79 | }, 80 | plotOptions: { 81 | series: { 82 | dataLabels: { 83 | color: '#282828' 84 | }, 85 | marker: { 86 | lineColor: '#333' 87 | } 88 | }, 89 | boxplot: { 90 | fillColor: '#505053' 91 | }, 92 | candlestick: { 93 | lineColor: 'white' 94 | }, 95 | errorbar: { 96 | color: 'white' 97 | } 98 | }, 99 | legend: { 100 | itemStyle: { 101 | color: '#050505' 102 | }, 103 | itemHoverStyle: { 104 | color: '#FFF' 105 | }, 106 | itemHiddenStyle: { 107 | color: '#606063' 108 | } 109 | }, 110 | credits: { 111 | style: { 112 | color: '#666' 113 | } 114 | }, 115 | labels: { 116 | style: { 117 | color: '#43CD80' 118 | } 119 | }, 120 | 121 | drilldown: { 122 | activeAxisLabelStyle: { 123 | color: '#F0F0F3' 124 | }, 125 | activeDataLabelStyle: { 126 | color: '#F0F0F3' 127 | } 128 | }, 129 | 130 | navigation: { 131 | buttonOptions: { 132 | symbolStroke: '#DDDDDD', 133 | theme: { 134 | fill: '#505053' 135 | } 136 | } 137 | }, 138 | 139 | // scroll charts 140 | rangeSelector: { 141 | buttonTheme: { 142 | fill: '#505053', 143 | stroke: '#000000', 144 | style: { 145 | color: '#CCC' 146 | }, 147 | states: { 148 | hover: { 149 | fill: '#43CD80', 150 | stroke: '#000000', 151 | style: { 152 | color: 'white' 153 | } 154 | }, 155 | select: { 156 | fill: '#000003', 157 | stroke: '#000000', 158 | style: { 159 | color: 'white' 160 | } 161 | } 162 | } 163 | }, 164 | inputBoxBorderColor: '#505053', 165 | inputStyle: { 166 | backgroundColor: '#333', 167 | color: 'silver' 168 | }, 169 | labelStyle: { 170 | color: 'silver' 171 | } 172 | }, 173 | 174 | navigator: { 175 | handles: { 176 | backgroundColor: '#666', 177 | borderColor: '#AAA' 178 | }, 179 | outlineColor: '#CCC', 180 | maskFill: 'rgba(255,255,255,0.1)', 181 | series: { 182 | color: '#7798BF', 183 | lineColor: '#A6C7ED' 184 | }, 185 | xAxis: { 186 | gridLineColor: '#505053' 187 | } 188 | }, 189 | 190 | scrollbar: { 191 | barBackgroundColor: '#808083', 192 | barBorderColor: '#808083', 193 | buttonArrowColor: '#CCC', 194 | buttonBackgroundColor: '#606063', 195 | buttonBorderColor: '#606063', 196 | rifleColor: '#FFF', 197 | trackBackgroundColor: '#404043', 198 | trackBorderColor: '#404043' 199 | }, 200 | 201 | // special colors for some of the 202 | legendBackgroundColor: 'rgba(0, 0, 0, 0.5)', 203 | background2: '#505053', 204 | dataLabelsColor: '#B0B0B3', 205 | textColor: '#000000', 206 | contrastTextColor: '#F0F0F3', 207 | maskColor: 'rgba(255,255,255,0.3)' 208 | }; 209 | 210 | // Apply the theme 211 | Highcharts.setOptions(Highcharts.theme); 212 | -------------------------------------------------------------------------------- /public/javascripts/initialize-tooltips.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | $(document).ready(function(){ 19 | $("[data-toggle=tooltip]").tooltip({container: 'body'}); 20 | }); 21 | 22 | -------------------------------------------------------------------------------- /public/javascripts/jquery.edatatables.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by jhazheng on 16/9/14. 3 | */ 4 | 5 | /* Set the defaults for DataTables initialisation */ 6 | $.extend( true, $.fn.dataTable.defaults, { 7 | "sDom": 8 | "<'row'<'col-xs-6'l><'col-xs-6'f>r>"+ 9 | "t"+ 10 | "<'row'<'col-xs-6'i><'col-xs-6'p>>", 11 | "sPaginationType": "bootstrap", 12 | "oLanguage": { 13 | "sLengthMenu": "_MENU_ 记录/每页" 14 | } 15 | } ); 16 | 17 | 18 | /* Default class modification */ 19 | $.extend( $.fn.dataTableExt.oStdClasses, { 20 | "sWrapper": "dataTables_wrapper form-inline", 21 | "sFilterInput": "form-control input-sm", 22 | "sLengthSelect": "form-control input-sm" 23 | } ); 24 | 25 | 26 | /* API method to get paging information */ 27 | $.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings ) 28 | { 29 | return { 30 | "iStart": oSettings._iDisplayStart, 31 | "iEnd": oSettings.fnDisplayEnd(), 32 | "iLength": oSettings._iDisplayLength, 33 | "iTotal": oSettings.fnRecordsTotal(), 34 | "iFilteredTotal": oSettings.fnRecordsDisplay(), 35 | "iPage": oSettings._iDisplayLength === -1 ? 36 | 0 : Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ), 37 | "iTotalPages": oSettings._iDisplayLength === -1 ? 38 | 0 : Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength ) 39 | }; 40 | }; 41 | 42 | 43 | /* Bootstrap style pagination control */ 44 | $.extend( $.fn.dataTableExt.oPagination, { 45 | "bootstrap": { 46 | "fnInit": function( oSettings, nPaging, fnDraw ) { 47 | var oLang = oSettings.oLanguage.oPaginate; 48 | var fnClickHandler = function ( e ) { 49 | e.preventDefault(); 50 | if ( oSettings.oApi._fnPageChange(oSettings, e.data.action) ) { 51 | fnDraw( oSettings ); 52 | } 53 | }; 54 | 55 | $(nPaging).addClass('pagination').append( 56 | '' 60 | ); 61 | var els = $('a', nPaging); 62 | $(els[0]).bind( 'click.DT', { action: "previous" }, fnClickHandler ); 63 | $(els[1]).bind( 'click.DT', { action: "next" }, fnClickHandler ); 64 | }, 65 | 66 | "fnUpdate": function ( oSettings, fnDraw ) { 67 | var iListLength = 5; 68 | var oPaging = oSettings.oInstance.fnPagingInfo(); 69 | var an = oSettings.aanFeatures.p; 70 | var i, ien, j, sClass, iStart, iEnd, iHalf=Math.floor(iListLength/2); 71 | 72 | if ( oPaging.iTotalPages < iListLength) { 73 | iStart = 1; 74 | iEnd = oPaging.iTotalPages; 75 | } 76 | else if ( oPaging.iPage <= iHalf ) { 77 | iStart = 1; 78 | iEnd = iListLength; 79 | } else if ( oPaging.iPage >= (oPaging.iTotalPages-iHalf) ) { 80 | iStart = oPaging.iTotalPages - iListLength + 1; 81 | iEnd = oPaging.iTotalPages; 82 | } else { 83 | iStart = oPaging.iPage - iHalf + 1; 84 | iEnd = iStart + iListLength - 1; 85 | } 86 | 87 | for ( i=0, ien=an.length ; i'+j+'') 95 | .insertBefore( $('li:last', an[i])[0] ) 96 | .bind('click', function (e) { 97 | e.preventDefault(); 98 | oSettings._iDisplayStart = (parseInt($('a', this).text(),10)-1) * oPaging.iLength; 99 | fnDraw( oSettings ); 100 | } ); 101 | } 102 | 103 | // Add / remove disabled classes from the static elements 104 | if ( oPaging.iPage === 0 ) { 105 | $('li:first', an[i]).addClass('disabled'); 106 | } else { 107 | $('li:first', an[i]).removeClass('disabled'); 108 | } 109 | 110 | if ( oPaging.iPage === oPaging.iTotalPages-1 || oPaging.iTotalPages === 0 ) { 111 | $('li:last', an[i]).addClass('disabled'); 112 | } else { 113 | $('li:last', an[i]).removeClass('disabled'); 114 | } 115 | } 116 | } 117 | } 118 | } ); 119 | 120 | 121 | /* 122 | * TableTools Bootstrap compatibility 123 | * Required TableTools 2.1+ 124 | */ 125 | if ( $.fn.DataTable.TableTools ) { 126 | // Set the classes that TableTools uses to something suitable for Bootstrap 127 | $.extend( true, $.fn.DataTable.TableTools.classes, { 128 | "container": "DTTT btn-group", 129 | "buttons": { 130 | "normal": "btn", 131 | "disabled": "disabled" 132 | }, 133 | "collection": { 134 | "container": "DTTT_dropdown dropdown-menu", 135 | "buttons": { 136 | "normal": "", 137 | "disabled": "disabled" 138 | } 139 | }, 140 | "print": { 141 | "info": "DTTT_print_info modal" 142 | }, 143 | "select": { 144 | "row": "active" 145 | } 146 | } ); 147 | 148 | // Have the collection use a bootstrap compatible dropdown 149 | $.extend( true, $.fn.DataTable.TableTools.DEFAULTS.oTags, { 150 | "collection": { 151 | "container": "ul", 152 | "button": "li", 153 | "liner": "a" 154 | } 155 | } ); 156 | } 157 | 158 | -------------------------------------------------------------------------------- /public/javascripts/progress.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 纯js进度条 3 | * Created by kiner on 15/3/22. 4 | */ 5 | 6 | function progress(options){ 7 | 8 | this.w = (options && options.width)?parseFloat(options.width) : parseFloat(this.options.width); 9 | this.h = (options && options.height)?parseFloat(options.height) : parseFloat(this.options.height); 10 | this.bgColor = (options && options.bgColor)?options.bgColor : this.options.bgColor; 11 | this.proColor = (options && options.proColor)?options.proColor : this.options.proColor; 12 | this.fontColor = (options && options.fontColor)?options.fontColor : this.options.fontColor; 13 | this.showPresent = (options && options.showPresent != undefined)?options.showPresent : this.options.showPresent; 14 | this.completeCallback = (options && options.completeCallback)?options.completeCallback : this.options.completeCallback; 15 | this.changeCallback = (options && options.changeCallback)?options.changeCallback : this.options.changeCallback; 16 | this.text = (options && options.text)?options.text : this.options.text; 17 | this.val = (options && options.val)?options.val : this.options.val; 18 | 19 | this.strTemp = this.text.substring(0,this.text.indexOf('#*'))+"{{pro}}"+this.text.substring(this.text.indexOf('*#')+2); 20 | 21 | this.init(); 22 | 23 | } 24 | /** 25 | * 默认选项 26 | * @type {{width: number, height: number, bgColor: string, proColor: string, fontColor: string, val: number, text: string, showPresent: boolean, completeCallback: Function, changeCallback: Function}} 27 | */ 28 | progress.prototype.options = { 29 | 30 | width : 200, 31 | height: 30, 32 | bgColor : "#005538", 33 | proColor : "#009988", 34 | fontColor : "#FFFFFF", 35 | val : 10, 36 | text:"当前进度为#*val*#%", 37 | showPresent : true, 38 | completeCallback:function(){}, 39 | changeCallback:function(){} 40 | 41 | }; 42 | 43 | /** 44 | * 初始化 45 | */ 46 | progress.prototype.init = function(){ 47 | 48 | this.proBox = document.createElement('div'); 49 | this.proBg = document.createElement('div'); 50 | this.proPre = document.createElement('div'); 51 | this.proFont = document.createElement('div'); 52 | 53 | addClass(this.proBox,'proBox'); 54 | addClass(this.proBg,'proBg'); 55 | addClass(this.proPre,'proPre'); 56 | addClass(this.proFont,'proFont'); 57 | 58 | this.proBox.setAttribute("style","width:"+this.w+"px; height:"+this.h+"px; position:relative; overflow:hidden; box-shadow:0 0 5px #FFFFFF; -moz-box-shadow:0 0 5px #FFFFFF; -webkit-box-shadow:0 0 5px #FFFFFF; -o-box-shadow:0 0 5px #FFFFFF;"); 59 | this.proBg.setAttribute("style","background-color:"+this.bgColor+"; position:absolute; z-index:1; width:100%; height:100%; top:0; left:0;"); 60 | this.proPre.setAttribute("style","transition:all 300ms; -moz-transition:all 300ms; -webkit-transition:all 300ms; -o-transition:all 300ms; width:"+this.val+"%; height:100%; background-color:"+this.proColor+"; position:absolute; z-index:2; top:0; left:0;"); 61 | if(this.showPresent){ 62 | 63 | this.proFont.setAttribute("style","overflow:hidden;text-overflow:ellipsis; white-space:nowrap; *white-space:nowrap; width:100%; height:100%; color:"+this.fontColor+"; text-align:center; line-height:"+this.h+"px; z-index:3; position:absolute; font-size:12px;"); 64 | 65 | var text = this.parseText(); 66 | this.proFont.innerHTML = text; 67 | this.proFont.setAttribute("title",text); 68 | this.proBox.appendChild(this.proFont); 69 | } 70 | 71 | 72 | this.proBox.appendChild(this.proBg); 73 | this.proBox.appendChild(this.proPre); 74 | 75 | }; 76 | 77 | /** 78 | * 79 | */ 80 | progress.prototype.refresh = function(){ 81 | this.proPre.style.width = this.val+"%"; 82 | 83 | this.proFont.innerHTML = this.parseText(); 84 | }; 85 | 86 | /** 87 | * 转换文字 88 | * @returns {options.text|*} 89 | */ 90 | progress.prototype.parseText = function(){ 91 | this.text = this.strTemp.replace("{{pro}}",this.val); 92 | return this.text; 93 | }; 94 | 95 | /** 96 | * 更新进度条进度 97 | * @param val 98 | */ 99 | progress.prototype.update = function(val){ 100 | 101 | this.val = val; 102 | this.refresh(); 103 | 104 | this.changeCallback.call(this,val); 105 | if(val==100){ 106 | 107 | this.completeCallback.call(this,val); 108 | 109 | } 110 | }; 111 | /** 112 | * 获取进度条本身的html对象,可直接将其塞入容器中 113 | * @returns {HTMLElement|*} 114 | */ 115 | progress.prototype.getBody = function(){ 116 | return this.proBox; 117 | }; 118 | /** 119 | * 获取当前进度条的值 120 | * @returns {*} 121 | */ 122 | progress.prototype.getVal = function(){ 123 | return this.val; 124 | }; 125 | 126 | function hasClass(obj, cls) { 127 | return obj.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); 128 | } 129 | 130 | function addClass(obj, cls) { 131 | if (!this.hasClass(obj, cls)) obj.className += " " + cls; 132 | } 133 | 134 | function removeClass(obj, cls) { 135 | if (hasClass(obj, cls)) { 136 | var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); 137 | obj.className = obj.className.replace(reg, ' '); 138 | } 139 | } 140 | 141 | function toggleClass(obj,cls){ 142 | if(hasClass(obj,cls)){ 143 | removeClass(obj, cls); 144 | }else{ 145 | addClass(obj, cls); 146 | } 147 | } -------------------------------------------------------------------------------- /public/javascripts/push.js: -------------------------------------------------------------------------------- 1 | var host = window.location.host 2 | var wsUri = "ws://" + host + "/"; 3 | var websocket = null 4 | var msg; 5 | 6 | 7 | function initWebSocket() 8 | { 9 | count = document.getElementById("count") 10 | createWebSocket(); 11 | } 12 | 13 | function createWebSocket() 14 | { 15 | websocket = new WebSocket(wsUri+"startpush"); 16 | websocket.onopen = function(evt) { onOpen(evt) }; 17 | websocket.onclose = function(evt) { onClose(evt) }; 18 | websocket.onmessage = function(evt) { onMessage(evt) }; 19 | websocket.onerror = function(evt) { onError(evt) }; 20 | } 21 | 22 | function onOpen(evt) 23 | { 24 | writeToScreen("CONNECTED"); 25 | doSend("WebSocket rocks"); 26 | } 27 | 28 | function onClose(evt) 29 | { 30 | writeToScreen("DISCONNECTED"); 31 | } 32 | 33 | function onMessage(evt) 34 | { 35 | writeToScreen('RESPONSE: ' + evt.data+''); 36 | showmessage(); 37 | } 38 | 39 | function showmessage(){ 40 | $.ajax({ 41 | type:"GET", 42 | url:"/msglist", 43 | dataType:"json", 44 | success:function(data){ 45 | var head= "" 46 | var $count = 0; 47 | var myobj=eval(data); 48 | for (var p in myobj) { 49 | var datamid = eval(data[p]); 50 | for (var i = 0; i < datamid.length; i++) { 51 | $count++; 52 | head +="
  • "+"End of the task to run, the current state "+datamid[i].state+""+"ID "+datamid[i].id+"
  • " 53 | 54 | }; 55 | }; 56 | 57 | $('#msglist').html(head); 58 | if($count!=0){ 59 | $('#count').html($count); 60 | } 61 | $('#mes').html($count); 62 | 63 | 64 | } 65 | }); 66 | 67 | } 68 | 69 | function onError(evt) 70 | { 71 | writeToScreen('ERROR: ' + evt.data); 72 | } 73 | 74 | function doSend(message) 75 | { 76 | writeToScreen("SENT: " + message); 77 | websocket.send(message); 78 | } 79 | 80 | function writeToScreen(message) 81 | { 82 | // var pre = document.createElement("p"); 83 | // pre.style.wordWrap = "break-word"; 84 | // pre.innerHTML = message; 85 | // output.appendChild(pre); 86 | } 87 | 88 | window.addEventListener("load", initWebSocket, false); -------------------------------------------------------------------------------- /public/javascripts/resizeable.js: -------------------------------------------------------------------------------- 1 | /* 2 | This function will be called in the event when browser breakpoint changes 3 | */ 4 | 5 | var public_vars = public_vars || {}; 6 | 7 | jQuery.extend(public_vars, { 8 | 9 | breakpoints: { 10 | largescreen: [991, -1], 11 | tabletscreen: [768, 990], 12 | devicescreen: [420, 767], 13 | sdevicescreen: [0, 419] 14 | }, 15 | 16 | lastBreakpoint: null 17 | }); 18 | 19 | 20 | /* Main Function that will be called each time when the screen breakpoint changes */ 21 | function resizable(breakpoint) 22 | { 23 | var sb_with_animation; 24 | 25 | // Large Screen Specific Script 26 | if(is('largescreen')) 27 | { 28 | 29 | } 30 | 31 | 32 | // Tablet or larger screen 33 | if(ismdxl()) 34 | { 35 | } 36 | 37 | 38 | // Tablet Screen Specific Script 39 | if(is('tabletscreen')) 40 | { 41 | } 42 | 43 | 44 | // Tablet device screen 45 | if(is('tabletscreen')) 46 | { 47 | public_vars.$sidebarMenu.addClass('collapsed'); 48 | ps_destroy(); 49 | } 50 | 51 | 52 | // Tablet Screen Specific Script 53 | if(isxs()) 54 | { 55 | } 56 | 57 | 58 | // Trigger Event 59 | jQuery(window).trigger('xenon.resize'); 60 | } 61 | 62 | 63 | 64 | /* Functions */ 65 | 66 | // Get current breakpoint 67 | function get_current_breakpoint() 68 | { 69 | var width = jQuery(window).width(), 70 | breakpoints = public_vars.breakpoints; 71 | 72 | for(var breakpont_label in breakpoints) 73 | { 74 | var bp_arr = breakpoints[breakpont_label], 75 | min = bp_arr[0], 76 | max = bp_arr[1]; 77 | 78 | if(max == -1) 79 | max = width; 80 | 81 | if(min <= width && max >= width) 82 | { 83 | return breakpont_label; 84 | } 85 | } 86 | 87 | return null; 88 | } 89 | 90 | 91 | // Check current screen breakpoint 92 | function is(screen_label) 93 | { 94 | return get_current_breakpoint() == screen_label; 95 | } 96 | 97 | 98 | // Is xs device 99 | function isxs() 100 | { 101 | return is('devicescreen') || is('sdevicescreen'); 102 | } 103 | 104 | // Is md or xl 105 | function ismdxl() 106 | { 107 | return is('tabletscreen') || is('largescreen'); 108 | } 109 | 110 | 111 | // Trigger Resizable Function 112 | function trigger_resizable() 113 | { 114 | if(public_vars.lastBreakpoint != get_current_breakpoint()) 115 | { 116 | public_vars.lastBreakpoint = get_current_breakpoint(); 117 | resizable(public_vars.lastBreakpoint); 118 | } 119 | 120 | 121 | // Trigger Event (Repeated) 122 | jQuery(window).trigger('xenon.resized'); 123 | } -------------------------------------------------------------------------------- /public/javascripts/scripts.js: -------------------------------------------------------------------------------- 1 | 2 | (function() { 3 | "use strict"; 4 | 5 | // custom scrollbar 6 | 7 | $("html").niceScroll({styler:"fb",cursorcolor:"#65cea7", cursorwidth: '6', cursorborderradius: '0px', background: '#424f63', spacebarenabled:false, cursorborder: '0', zindex: '1000'}); 8 | 9 | $(".left-side").niceScroll({styler:"fb",cursorcolor:"#65cea7", cursorwidth: '3', cursorborderradius: '0px', background: '#424f63', spacebarenabled:false, cursorborder: '0'}); 10 | 11 | 12 | $(".left-side").getNiceScroll(); 13 | if ($('body').hasClass('left-side-collapsed')) { 14 | $(".left-side").getNiceScroll().hide(); 15 | } 16 | 17 | 18 | 19 | // Toggle Left Menu 20 | jQuery('.menu-list > a').click(function() { 21 | 22 | var parent = jQuery(this).parent(); 23 | var sub = parent.find('> ul'); 24 | 25 | if(!jQuery('body').hasClass('left-side-collapsed')) { 26 | if(sub.is(':visible')) { 27 | sub.slideUp(200, function(){ 28 | parent.removeClass('nav-active'); 29 | jQuery('.main-content').css({height: ''}); 30 | mainContentHeightAdjust(); 31 | }); 32 | } else { 33 | visibleSubMenuClose(); 34 | parent.addClass('nav-active'); 35 | sub.slideDown(200, function(){ 36 | mainContentHeightAdjust(); 37 | }); 38 | } 39 | } 40 | return false; 41 | }); 42 | 43 | function visibleSubMenuClose() { 44 | jQuery('.menu-list').each(function() { 45 | var t = jQuery(this); 46 | if(t.hasClass('nav-active')) { 47 | t.find('> ul').slideUp(200, function(){ 48 | t.removeClass('nav-active'); 49 | }); 50 | } 51 | }); 52 | } 53 | 54 | function mainContentHeightAdjust() { 55 | // Adjust main content height 56 | var docHeight = jQuery(document).height(); 57 | if(docHeight > jQuery('.main-content').height()) 58 | jQuery('.main-content').height(docHeight); 59 | } 60 | 61 | // class add mouse hover 62 | jQuery('.custom-nav > li').hover(function(){ 63 | jQuery(this).addClass('nav-hover'); 64 | }, function(){ 65 | jQuery(this).removeClass('nav-hover'); 66 | }); 67 | 68 | 69 | // Menu Toggle 70 | jQuery('.toggle-btn').click(function(){ 71 | $(".left-side").getNiceScroll().hide(); 72 | 73 | if ($('body').hasClass('left-side-collapsed')) { 74 | $(".left-side").getNiceScroll().hide(); 75 | } 76 | var body = jQuery('body'); 77 | var bodyposition = body.css('position'); 78 | 79 | if(bodyposition != 'relative') { 80 | 81 | if(!body.hasClass('left-side-collapsed')) { 82 | body.addClass('left-side-collapsed'); 83 | jQuery('.custom-nav ul').attr('style',''); 84 | 85 | jQuery(this).addClass('menu-collapsed'); 86 | 87 | } else { 88 | body.removeClass('left-side-collapsed chat-view'); 89 | jQuery('.custom-nav li.active ul').css({display: 'block'}); 90 | 91 | jQuery(this).removeClass('menu-collapsed'); 92 | 93 | } 94 | } else { 95 | 96 | if(body.hasClass('left-side-show')) 97 | body.removeClass('left-side-show'); 98 | else 99 | body.addClass('left-side-show'); 100 | 101 | mainContentHeightAdjust(); 102 | } 103 | 104 | }); 105 | 106 | 107 | searchform_reposition(); 108 | 109 | jQuery(window).resize(function(){ 110 | 111 | if(jQuery('body').css('position') == 'relative') { 112 | 113 | jQuery('body').removeClass('left-side-collapsed'); 114 | 115 | } else { 116 | 117 | jQuery('body').css({left: '', marginRight: ''}); 118 | } 119 | 120 | searchform_reposition(); 121 | 122 | }); 123 | 124 | function searchform_reposition() { 125 | if(jQuery('.searchform').css('position') == 'relative') { 126 | jQuery('.searchform').insertBefore('.left-side-inner .logged-user'); 127 | } else { 128 | jQuery('.searchform').insertBefore('.menu-right'); 129 | } 130 | } 131 | 132 | // panel collapsible 133 | $('.panel .tools .fa').click(function () { 134 | var el = $(this).parents(".panel").children(".panel-body"); 135 | if ($(this).hasClass("fa-chevron-down")) { 136 | $(this).removeClass("fa-chevron-down").addClass("fa-chevron-up"); 137 | el.slideUp(200); 138 | } else { 139 | $(this).removeClass("fa-chevron-up").addClass("fa-chevron-down"); 140 | el.slideDown(200); } 141 | }); 142 | 143 | $('.todo-check label').click(function () { 144 | $(this).parents('li').children('.todo-title').toggleClass('line-through'); 145 | }); 146 | 147 | $(document).on('click', '.todo-remove', function () { 148 | $(this).closest("li").remove(); 149 | return false; 150 | }); 151 | 152 | $("#sortable-todo").sortable(); 153 | 154 | 155 | // panel close 156 | $('.panel .tools .fa-times').click(function () { 157 | $(this).parents(".panel").parent().remove(); 158 | }); 159 | 160 | 161 | 162 | // tool tips 163 | 164 | $('.tooltips').tooltip(); 165 | 166 | // popovers 167 | 168 | $('.popovers').popover(); 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | })(jQuery); -------------------------------------------------------------------------------- /public/javascripts/xenon-api.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Xenon API Functions 3 | * 4 | * Theme by: www.laborator.co 5 | **/ 6 | 7 | 8 | function rtl() // checks whether the content is in RTL mode 9 | { 10 | if(typeof window.isRTL == 'boolean') 11 | return window.isRTL; 12 | 13 | window.isRTL = jQuery("html").get(0).dir == 'rtl' ? true : false; 14 | 15 | return window.isRTL; 16 | } 17 | 18 | 19 | 20 | // Page Loader 21 | function show_loading_bar(options) 22 | { 23 | var defaults = { 24 | pct: 0, 25 | delay: 1.3, 26 | wait: 0, 27 | before: function(){}, 28 | finish: function(){}, 29 | resetOnEnd: true 30 | }; 31 | 32 | if(typeof options == 'object') 33 | defaults = jQuery.extend(defaults, options); 34 | else 35 | if(typeof options == 'number') 36 | defaults.pct = options; 37 | 38 | 39 | if(defaults.pct > 100) 40 | defaults.pct = 100; 41 | else 42 | if(defaults.pct < 0) 43 | defaults.pct = 0; 44 | 45 | var $ = jQuery, 46 | $loading_bar = $(".xenon-loading-bar"); 47 | 48 | if($loading_bar.length == 0) 49 | { 50 | $loading_bar = $('
    '); 51 | public_vars.$body.append( $loading_bar ); 52 | } 53 | 54 | var $pct = $loading_bar.find('span'), 55 | current_pct = $pct.data('pct'), 56 | is_regress = current_pct > defaults.pct; 57 | 58 | 59 | defaults.before(current_pct); 60 | 61 | TweenMax.to($pct, defaults.delay, {css: {width: defaults.pct + '%'}, delay: defaults.wait, ease: is_regress ? Expo.easeOut : Expo.easeIn, 62 | onStart: function() 63 | { 64 | $loading_bar.removeClass('progress-is-hidden'); 65 | }, 66 | onComplete: function() 67 | { 68 | var pct = $pct.data('pct'); 69 | 70 | if(pct == 100 && defaults.resetOnEnd) 71 | { 72 | hide_loading_bar(); 73 | } 74 | 75 | defaults.finish(pct); 76 | }, 77 | onUpdate: function() 78 | { 79 | $pct.data('pct', parseInt($pct.get(0).style.width, 10)); 80 | }}); 81 | } 82 | 83 | function hide_loading_bar() 84 | { 85 | var $ = jQuery, 86 | $loading_bar = $(".xenon-loading-bar"), 87 | $pct = $loading_bar.find('span'); 88 | 89 | $loading_bar.addClass('progress-is-hidden'); 90 | $pct.width(0).data('pct', 0); 91 | } -------------------------------------------------------------------------------- /public/javascripts/xenon-widgets.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Xenon Widgets 3 | * 4 | * Theme by: www.laborator.co 5 | **/ 6 | 7 | ;(function($, window, undefined){ 8 | 9 | "use strict"; 10 | 11 | $(document).ready(function() 12 | { 13 | if($('.page-loading-overlay').length) 14 | { 15 | $(window).on('load', function() 16 | { 17 | setTimeout(setupWidgets, 200); 18 | }); 19 | } 20 | else 21 | { 22 | setupWidgets(); 23 | } 24 | }); 25 | 26 | 27 | var setupWidgets = function() 28 | { 29 | // Count Anything 30 | $("[data-from][data-to]").each(function(i, el) 31 | { 32 | var $el = $(el), 33 | sm = scrollMonitor.create(el); 34 | 35 | sm.fullyEnterViewport(function() 36 | { 37 | var opts = { 38 | useEasing: attrDefault($el, 'easing', true), 39 | useGrouping: attrDefault($el, 'grouping', true), 40 | separator: attrDefault($el, 'separator', ','), 41 | decimal: attrDefault($el, 'decimal', '.'), 42 | prefix: attrDefault($el, 'prefix', ''), 43 | suffix: attrDefault($el, 'suffix', ''), 44 | }, 45 | $count = attrDefault($el, 'count', 'this') == 'this' ? $el : $el.find($el.data('count')), 46 | from = attrDefault($el, 'from', 0), 47 | to = attrDefault($el, 'to', 100), 48 | duration = attrDefault($el, 'duration', 2.5), 49 | delay = attrDefault($el, 'delay', 0), 50 | decimals = new String(to).match(/\.([0-9]+)/) ? new String(to).match(/\.([0-9]+)$/)[1].length : 0, 51 | counter = new countUp($count.get(0), from, to, decimals, duration, opts); 52 | 53 | setTimeout(function(){ counter.start(); }, delay * 1000); 54 | 55 | sm.destroy(); 56 | }); 57 | }); 58 | 59 | 60 | // Fill Anything 61 | $("[data-fill-from][data-fill-to]").each(function(i, el) 62 | { 63 | var $el = $(el), 64 | sm = scrollMonitor.create(el); 65 | 66 | sm.fullyEnterViewport(function() 67 | { 68 | var fill = { 69 | current: null, 70 | from: attrDefault($el, 'fill-from', 0), 71 | to: attrDefault($el, 'fill-to', 100), 72 | property: attrDefault($el, 'fill-property', 'width'), 73 | unit: attrDefault($el, 'fill-unit', '%'), 74 | }, 75 | opts = { 76 | current: fill.to, onUpdate: function(){ 77 | $el.css(fill.property, fill.current + fill.unit); 78 | }, 79 | delay: attrDefault($el, 'delay', 0), 80 | }, 81 | easing = attrDefault($el, 'fill-easing', true), 82 | duration = attrDefault($el, 'fill-duration', 2.5); 83 | 84 | if(easing) 85 | { 86 | opts.ease = Sine.easeOut; 87 | } 88 | 89 | // Set starting point 90 | fill.current = fill.from; 91 | 92 | TweenMax.to(fill, duration, opts); 93 | 94 | sm.destroy(); 95 | }); 96 | }); 97 | 98 | 99 | // Todo List 100 | $(".xe-todo-list").on('change', 'input[type="checkbox"]', function(ev) 101 | { 102 | var $cb = $(this), 103 | $li = $cb.closest('li'); 104 | 105 | $li.removeClass('done'); 106 | 107 | if($cb.is(':checked')) 108 | { 109 | $li.addClass('done'); 110 | } 111 | }); 112 | 113 | 114 | $(".xe-status-update").each(function(i, el) 115 | { 116 | var $el = $(el), 117 | $nav = $el.find('.xe-nav a'), 118 | $status_list = $el.find('.xe-body li'), 119 | index = $status_list.filter('.active').index(), 120 | auto_switch = attrDefault($el, 'auto-switch', 0), 121 | as_interval = 0; 122 | 123 | if(auto_switch > 0) 124 | { 125 | as_interval = setInterval(function() 126 | { 127 | goTo(1); 128 | 129 | }, auto_switch * 1000); 130 | 131 | $el.hover(function() 132 | { 133 | window.clearInterval(as_interval); 134 | }, 135 | function() 136 | { 137 | as_interval = setInterval(function() 138 | { 139 | goTo(1); 140 | 141 | }, auto_switch * 1000);; 142 | }); 143 | } 144 | 145 | function goTo(plus_one) 146 | { 147 | index = (index + plus_one) % $status_list.length; 148 | 149 | if(index < 0) 150 | index = $status_list.length - 1; 151 | 152 | var $to_hide = $status_list.filter('.active'), 153 | $to_show = $status_list.eq(index); 154 | 155 | $to_hide.removeClass('active'); 156 | $to_show.addClass('active').fadeTo(0,0).fadeTo(320,1); 157 | } 158 | 159 | $nav.on('click', function(ev) 160 | { 161 | ev.preventDefault(); 162 | 163 | var plus_one = $(this).hasClass('xe-prev') ? -1 : 1; 164 | 165 | goTo(plus_one); 166 | }); 167 | }); 168 | } 169 | 170 | 171 | })(jQuery, window); -------------------------------------------------------------------------------- /public/stylesheets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/stylesheets/.DS_Store -------------------------------------------------------------------------------- /public/stylesheets/DT_bootstrap.css: -------------------------------------------------------------------------------- 1 | 2 | div.dataTables_length label { 3 | float: left; 4 | text-align: left; 5 | } 6 | 7 | div.dataTables_length select { 8 | width: 75px; 9 | } 10 | 11 | div.dataTables_filter label { 12 | float: right; 13 | } 14 | 15 | div.dataTables_info { 16 | padding-top: 8px; 17 | } 18 | 19 | div.dataTables_paginate { 20 | float: right; 21 | margin: 0; 22 | } 23 | 24 | table.table { 25 | clear: both; 26 | margin-bottom: 6px !important; 27 | max-width: none !important; 28 | } 29 | 30 | table.table thead .sorting, 31 | table.table thead .sorting_asc, 32 | table.table thead .sorting_desc, 33 | table.table thead .sorting_asc_disabled, 34 | table.table thead .sorting_desc_disabled { 35 | cursor: pointer; 36 | *cursor: hand; 37 | } 38 | 39 | table.table thead .sorting { background: url('../images/sort_both.png') no-repeat center right; } 40 | table.table thead .sorting_asc { background: url('../images/sort_asc.png') no-repeat center right; } 41 | table.table thead .sorting_desc { background: url('../images/sort_desc.png') no-repeat center right; } 42 | 43 | table.table thead .sorting_asc_disabled { background: url('../images/sort_asc_disabled.png') no-repeat center right; } 44 | table.table thead .sorting_desc_disabled { background: url('../images/sort_desc_disabled.png') no-repeat center right; } 45 | 46 | table.dataTable th:active { 47 | outline: none; 48 | } 49 | 50 | /* Scrolling */ 51 | div.dataTables_scrollHead table { 52 | margin-bottom: 0 !important; 53 | border-bottom-left-radius: 0; 54 | border-bottom-right-radius: 0; 55 | } 56 | 57 | div.dataTables_scrollHead table thead tr:last-child th:first-child, 58 | div.dataTables_scrollHead table thead tr:last-child td:first-child { 59 | border-bottom-left-radius: 0 !important; 60 | border-bottom-right-radius: 0 !important; 61 | } 62 | 63 | div.dataTables_scrollBody table { 64 | border-top: none; 65 | margin-bottom: 0 !important; 66 | } 67 | 68 | div.dataTables_scrollBody tbody tr:first-child th, 69 | div.dataTables_scrollBody tbody tr:first-child td { 70 | border-top: none; 71 | } 72 | 73 | div.dataTables_scrollFoot table { 74 | border-top: none; 75 | } 76 | 77 | 78 | 79 | 80 | /* 81 | * TableTools styles 82 | */ 83 | .table tbody tr.active td, 84 | .table tbody tr.active th { 85 | background-color: #08C; 86 | color: white; 87 | } 88 | 89 | .table tbody tr.active:hover td, 90 | .table tbody tr.active:hover th { 91 | background-color: #0075b0 !important; 92 | } 93 | 94 | .table-striped tbody tr.active:nth-child(odd) td, 95 | .table-striped tbody tr.active:nth-child(odd) th { 96 | background-color: #017ebc; 97 | } 98 | 99 | table.DTTT_selectable tbody tr { 100 | cursor: pointer; 101 | *cursor: hand; 102 | } 103 | 104 | div.DTTT .btn { 105 | color: #333 !important; 106 | font-size: 12px; 107 | } 108 | 109 | div.DTTT .btn:hover { 110 | text-decoration: none !important; 111 | } 112 | 113 | 114 | ul.DTTT_dropdown.dropdown-menu a { 115 | color: #333 !important; /* needed only when demo_page.css is included */ 116 | } 117 | 118 | ul.DTTT_dropdown.dropdown-menu li:hover a { 119 | background-color: #0088cc; 120 | color: white !important; 121 | } 122 | 123 | /* TableTools information display */ 124 | div.DTTT_print_info.modal { 125 | height: 150px; 126 | margin-top: -75px; 127 | text-align: center; 128 | } 129 | 130 | div.DTTT_print_info h6 { 131 | font-weight: normal; 132 | font-size: 28px; 133 | line-height: 28px; 134 | margin: 1em; 135 | } 136 | 137 | div.DTTT_print_info p { 138 | font-size: 14px; 139 | line-height: 20px; 140 | } 141 | 142 | 143 | 144 | /* 145 | * FixedColumns styles 146 | */ 147 | div.DTFC_LeftHeadWrapper table, 148 | div.DTFC_LeftFootWrapper table, 149 | table.DTFC_Cloned tr.even { 150 | background-color: white; 151 | } 152 | 153 | div.DTFC_LeftHeadWrapper table { 154 | margin-bottom: 0 !important; 155 | border-top-right-radius: 0 !important; 156 | border-bottom-left-radius: 0 !important; 157 | border-bottom-right-radius: 0 !important; 158 | } 159 | 160 | div.DTFC_LeftHeadWrapper table thead tr:last-child th:first-child, 161 | div.DTFC_LeftHeadWrapper table thead tr:last-child td:first-child { 162 | border-bottom-left-radius: 0 !important; 163 | border-bottom-right-radius: 0 !important; 164 | } 165 | 166 | div.DTFC_LeftBodyWrapper table { 167 | border-top: none; 168 | margin-bottom: 0 !important; 169 | } 170 | 171 | div.DTFC_LeftBodyWrapper tbody tr:first-child th, 172 | div.DTFC_LeftBodyWrapper tbody tr:first-child td { 173 | border-top: none; 174 | } 175 | 176 | div.DTFC_LeftFootWrapper table { 177 | border-top: none; 178 | } 179 | -------------------------------------------------------------------------------- /public/stylesheets/bootstrap-reset.css: -------------------------------------------------------------------------------- 1 | 2 | .nav-stacked > li + li { 3 | margin-top: 1px; 4 | } 5 | 6 | .nav > li > a:hover, .nav > li > a:focus { 7 | background-color: #353F4F; 8 | text-decoration: none; 9 | } 10 | 11 | .panel { 12 | border: none; 13 | } 14 | 15 | .panel-heading { 16 | border-bottom: 1px dotted rgba(0, 0, 0, 0.2); 17 | padding: 15px; 18 | text-transform: uppercase; 19 | color: #535351; 20 | font-size: 14px; 21 | font-weight: bold; 22 | } 23 | 24 | /*dropdown shadow*/ 25 | 26 | .btn-group.open .dropdown-toggle, .btn-white.active, .btn:active, .btn.active { 27 | box-shadow: none; 28 | } 29 | 30 | /*dropdown select bg*/ 31 | 32 | .dropdown-menu { 33 | box-shadow: none; 34 | } 35 | .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { 36 | background-color: #424F63; 37 | color: #FFFFFF; 38 | text-decoration: none; 39 | } 40 | 41 | /*progress*/ 42 | 43 | .progress { 44 | box-shadow: none; 45 | background: #f0f2f7; 46 | height: 15px; 47 | border-radius: 3px; 48 | -webkit-border-radius: 3px; 49 | } 50 | 51 | .progress-bar { 52 | box-shadow: none; 53 | line-height: 15px; 54 | text-align: right; 55 | padding-right: 10px; 56 | font-size: 11px; 57 | } 58 | 59 | /*alert*/ 60 | 61 | .alert-success, .alert-danger, .alert-info, .alert-warning { 62 | border: none; 63 | } 64 | 65 | /*table*/ 66 | 67 | .table thead > tr > th, .table tbody > tr > th, .table tfoot > tr > th, .table thead > tr > td, .table tbody > tr > td, .table tfoot > tr > td { 68 | padding: 10px; 69 | } 70 | 71 | /*pagination*/ 72 | 73 | .pagination > li > a, .pagination > li > span { 74 | background-color: #EFF2F7; 75 | border: 1px solid #EFF2F7; 76 | float: left; 77 | line-height: 1.42857; 78 | margin-left: 1px; 79 | padding: 6px 12px; 80 | position: relative; 81 | text-decoration: none; 82 | color: #535351; 83 | } 84 | 85 | .pagination > li > a:hover, 86 | .pagination > li > span:hover, 87 | .pagination > li > a:focus, 88 | .pagination > li > span:focus, 89 | .pagination > li.active > a, 90 | .pagination > li.active > a:hover, 91 | 92 | .pager li > a:hover, .pager li > a:focus{ 93 | background-color: #65CEA7; 94 | border-color: #65CEA7; 95 | color: #fff; 96 | } 97 | 98 | .pager li > a, .pager li > span { 99 | background-color: #EFF2F7; 100 | border: 1px solid #EFF2F7; 101 | color: #535351; 102 | } 103 | 104 | /*button*/ 105 | 106 | .btn-primary { 107 | background-color: #424F63; 108 | border-color: #374152; 109 | color: #FFFFFF; 110 | } 111 | 112 | .btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open .dropdown-toggle.btn-primary { 113 | background-color: #374152; 114 | border-color: #2e3644; 115 | color: #FFFFFF; 116 | } 117 | 118 | 119 | /*modal*/ 120 | 121 | .modal-content { 122 | box-shadow: none; 123 | border: none; 124 | border-radius: 0; 125 | -webkit-border-radius: 0; 126 | } 127 | 128 | .modal-header { 129 | background: #65CEA7; 130 | color: #fff; 131 | border-radius: 0; 132 | -webkit-border-radius: 0; 133 | border: none; 134 | } 135 | 136 | .modal-header .close { 137 | color: #fff; 138 | opacity: .5; 139 | } 140 | .modal-header .close:hover { 141 | opacity: 1; 142 | } 143 | 144 | /*popover*/ 145 | 146 | .popover { 147 | box-shadow: none; 148 | } 149 | 150 | /*form elements*/ 151 | 152 | .form-group label { 153 | font-weight: normal; 154 | } 155 | 156 | .form-control { 157 | box-shadow: none; 158 | } 159 | 160 | .form-control:focus, #focusedInput { 161 | border: 1px solid #424F63; 162 | box-shadow: none; 163 | } 164 | 165 | .has-success .form-control:focus, 166 | .has-warning .form-control:focus, 167 | .has-error .form-control:focus{ 168 | box-shadow: none; 169 | } 170 | 171 | /*panels*/ 172 | 173 | .panel-default > .panel-heading { 174 | background-color: #424F63; 175 | border-color: #424F63; 176 | color: #fff; 177 | } 178 | 179 | .panel-success > .panel-heading { 180 | background-color: #5CB85C; 181 | border-color: #5CB85C; 182 | color: #fff; 183 | } 184 | 185 | .panel-info > .panel-heading { 186 | background-color: #46B8DA; 187 | border-color: #46B8DA; 188 | color: #fff; 189 | } 190 | 191 | .panel-warning > .panel-heading { 192 | background-color: #F0AD4E; 193 | border-color: #F0AD4E; 194 | color: #fff; 195 | } 196 | 197 | .panel-danger > .panel-heading { 198 | background-color: #D9534F; 199 | border-color: #D9534F; 200 | color: #fff; 201 | } -------------------------------------------------------------------------------- /public/stylesheets/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Apply your custom CSS here 4 | * 5 | */ 6 | 7 | #my-cc .nanobar .bar { 8 | background: #6bff63 ;; 9 | } -------------------------------------------------------------------------------- /public/stylesheets/google.css: -------------------------------------------------------------------------------- 1 | .sweet-overlay { 2 | background: rgba(10, 10, 10, 0.6); } 3 | 4 | .sweet-alert { 5 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 6 | padding: 24px; 7 | padding-top: 64px; 8 | padding-bottom: 13px; 9 | text-align: right; 10 | border-radius: 0; 11 | box-shadow: 0 0 14px rgba(0, 0, 0, 0.24), 0 14px 28px rgba(0, 0, 0, 0.48); } 12 | .sweet-alert h2 { 13 | position: absolute; 14 | top: 0; 15 | left: 0; 16 | right: 0; 17 | height: auto; 18 | font-weight: 400; 19 | color: #212121; 20 | margin: 20px 0; 21 | font-size: 1.2em; 22 | line-height: 1.25; 23 | text-align: left; 24 | padding: 0 24px; } 25 | .sweet-alert p { 26 | display: block; 27 | text-align: center; 28 | color: #212121; 29 | font-weight: 400; 30 | font-size: 14px; 31 | margin: 20px 0; } 32 | .sweet-alert button { 33 | border-radius: 2px; 34 | box-shadow: none !important; 35 | background: none !important; 36 | border-radius: 2px; 37 | text-transform: uppercase; 38 | font-size: 14px; 39 | font-weight: 600; 40 | padding: 8px 16px; 41 | position: relative; 42 | margin-top: 0; } 43 | .sweet-alert button:hover, .sweet-alert button:focus { 44 | background-color: #f6f6f6 !important; } 45 | .sweet-alert button.confirm { 46 | color: #3c80f6; } 47 | .sweet-alert button.cancel { 48 | color: #757575; } 49 | .sweet-alert button.cancel:focus { 50 | box-shadow: none !important; } 51 | .sweet-alert .sa-icon:not(.sa-custom) { 52 | transform: scale(0.8); 53 | margin-bottom: -10px; 54 | margin-top: -10px; } 55 | .sweet-alert input { 56 | border: none; 57 | border-radius: 0; 58 | border-bottom: 1px solid #c9c9c9; 59 | color: #212121; 60 | margin-bottom: 8px; 61 | padding: 1px; 62 | padding-bottom: 8px; 63 | height: auto; 64 | box-shadow: none; 65 | font-size: 13px; 66 | margin: 10px 0; } 67 | .sweet-alert input:focus { 68 | border: none; 69 | border-bottom: 1px solid #3c80f6; 70 | box-shadow: inset 0 -1px 0 #3c80f6; } 71 | .sweet-alert fieldset { 72 | padding: 0; } 73 | .sweet-alert fieldset .sa-input-error { 74 | display: none; } 75 | .sweet-alert .sa-error-container { 76 | display: none; 77 | background: none; 78 | height: auto; 79 | padding: 0 24px; 80 | margin: 0 -20px; 81 | text-align: left; } 82 | .sweet-alert .sa-error-container.show { 83 | padding: 0 24px; 84 | display: block; } 85 | .sweet-alert .sa-error-container.show ~ fieldset input { 86 | background: red; 87 | border-bottom: 1px solid #d9453c; 88 | box-shadow: inset 0 -1px 0 #d9453c; } 89 | .sweet-alert .sa-error-container .icon { 90 | display: none; } 91 | .sweet-alert .sa-error-container p { 92 | color: #d9453c; 93 | margin-top: 0; } 94 | 95 | @-webkit-keyframes animateErrorIcon { 96 | 0% { 97 | transform: rotateX(100deg), scale(0.5); 98 | -webkit-transform: rotateX(100deg), scale(0.5); 99 | opacity: 0; } 100 | 101 | 100% { 102 | transform: rotateX(0deg), scale(0.5); 103 | -webkit-transform: rotateX(0deg), scale(0.5); 104 | opacity: 1; } } 105 | 106 | @keyframes animateErrorIcon { 107 | 0% { 108 | transform: rotateX(100deg), scale(0.5); 109 | -webkit-transform: rotateX(100deg), scale(0.5); 110 | opacity: 0; } 111 | 112 | 100% { 113 | transform: rotateX(0deg), scale(0.5); 114 | -webkit-transform: rotateX(0deg), scale(0.5); 115 | opacity: 1; } } 116 | -------------------------------------------------------------------------------- /public/stylesheets/jquery-ui.min.css: -------------------------------------------------------------------------------- 1 | /*! jQuery UI - v1.11.1 - 2014-09-18 2 | * http://jqueryui.com 3 | * Includes: core.css, draggable.css, resizable.css, selectable.css, sortable.css, slider.css 4 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ 5 | 6 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0} -------------------------------------------------------------------------------- /public/stylesheets/lines.css: -------------------------------------------------------------------------------- 1 | 2 | .rickshaw_graph .detail { 3 | pointer-events: none; 4 | position: absolute; 5 | top: 0; 6 | z-index: 2; 7 | background: rgba(0, 0, 0, 0.1); 8 | bottom: 0; 9 | width: 1px; 10 | transition: opacity 0.25s linear; 11 | -moz-transition: opacity 0.25s linear; 12 | -o-transition: opacity 0.25s linear; 13 | -webkit-transition: opacity 0.25s linear; 14 | } 15 | .rickshaw_graph .detail.inactive { 16 | opacity: 0; 17 | } 18 | .rickshaw_graph .detail .item.active { 19 | opacity: 1; 20 | } 21 | .rickshaw_graph .detail .x_label { 22 | font-family: Arial, sans-serif; 23 | border-radius: 3px; 24 | padding: 6px; 25 | opacity: 0.5; 26 | border: 1px solid #e0e0e0; 27 | font-size: 12px; 28 | position: absolute; 29 | background: white; 30 | white-space: nowrap; 31 | } 32 | .rickshaw_graph .detail .x_label.left { 33 | left: 0; 34 | } 35 | .rickshaw_graph .detail .x_label.right { 36 | right: 0; 37 | } 38 | .rickshaw_graph .detail .item { 39 | position: absolute; 40 | z-index: 2; 41 | border-radius: 3px; 42 | 43 | font-size: 12px; 44 | font-family: Arial, sans-serif; 45 | opacity: 0; 46 | background: rgba(0, 0, 0, 0.4); 47 | color: white; 48 | border: 1px solid rgba(0, 0, 0, 0.4); 49 | margin-left: 1em; 50 | margin-right: 1em; 51 | margin-top: -1em; 52 | white-space: nowrap; 53 | } 54 | .rickshaw_graph .detail .item.left { 55 | left: 0; 56 | } 57 | .rickshaw_graph .detail .item.right { 58 | right: 0; 59 | } 60 | .rickshaw_graph .detail .item.active { 61 | opacity: 1; 62 | background: rgba(0, 0, 0, 0.8); 63 | } 64 | .rickshaw_graph .detail .item:after { 65 | position: absolute; 66 | display: block; 67 | width: 0; 68 | height: 0; 69 | 70 | content: ""; 71 | 72 | border: 5px solid transparent; 73 | } 74 | .rickshaw_graph .detail .item.left:after { 75 | top: 1em; 76 | left: -5px; 77 | margin-top: -5px; 78 | border-right-color: rgba(0, 0, 0, 0.8); 79 | border-left-width: 0; 80 | } 81 | .rickshaw_graph .detail .item.right:after { 82 | top: 1em; 83 | right: -5px; 84 | margin-top: -5px; 85 | border-left-color: rgba(0, 0, 0, 0.8); 86 | border-right-width: 0; 87 | } 88 | .rickshaw_graph .detail .dot { 89 | width: 4px; 90 | height: 4px; 91 | margin-left: -3px; 92 | margin-top: -3.5px; 93 | border-radius: 5px; 94 | position: absolute; 95 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.6); 96 | box-sizing: content-box; 97 | -moz-box-sizing: content-box; 98 | background: white; 99 | border-width: 2px; 100 | border-style: solid; 101 | display: none; 102 | background-clip: padding-box; 103 | } 104 | .rickshaw_graph .detail .dot.active { 105 | display: block; 106 | } 107 | /* graph */ 108 | #chart { 109 | width: 649px !important; 110 | height: 400px !important; 111 | background-color: #fff; 112 | } 113 | .rickshaw_graph { 114 | position: relative; 115 | } 116 | .rickshaw_graph svg { 117 | display: block; 118 | overflow: hidden; 119 | } 120 | @media (max-width:1366px){ 121 | #chart { 122 | width:533px !important; 123 | } 124 | } 125 | @media (max-width:1280px){ 126 | #chart { 127 | width:483px !important; 128 | } 129 | canvas#doughnut, canvas#line, canvas#polarArea, #bar, #pie, #radar { 130 | width: 308px !important; 131 | height: 220px !important; 132 | } 133 | } 134 | @media (max-width:1024px){ 135 | #chart { 136 | width:360px !important; 137 | height:300px !important; 138 | } 139 | } 140 | @media (max-width:768px){ 141 | #chart { 142 | width:468px !important; 143 | height: 300px !important; 144 | } 145 | } 146 | @media (max-width:736px){ 147 | #chart { 148 | width:685px !important; 149 | height: 300px !important; 150 | } 151 | } 152 | @media (max-width:640px){ 153 | #chart { 154 | width:580px !important; 155 | height:250px !important; 156 | } 157 | } 158 | @media (max-width:540px){ 159 | #chart { 160 | width:464px !important; 161 | height:250px !important; 162 | } 163 | } 164 | @media (max-width:480px){ 165 | #chart { 166 | width:447px !important; 167 | height:250px !important; 168 | } 169 | } 170 | @media (max-width:320px){ 171 | #chart { 172 | width:298px !important; 173 | height:250px !important; 174 | } 175 | .copy { 176 | padding: 10px; 177 | } 178 | .copy p{ 179 | font-size:13px; 180 | } 181 | .btn-group-lg>.btn, .btn-lg { 182 | padding: 6px 7px; 183 | font-size: 16px; 184 | } 185 | .label { 186 | font-size: 56%; 187 | } 188 | .pagination-lg>li>a, .pagination-lg>li>span { 189 | padding: 8px 13px; 190 | font-size: 15px; 191 | } 192 | .nav>li>a { 193 | padding: 7px 8px; 194 | } 195 | .modal-dialog { 196 | width: 245px !important; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /public/stylesheets/meteocons.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'meteocons'; 3 | font-weight: normal; 4 | font-style: normal; 5 | } 6 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ 7 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ 8 | /* 9 | @media screen and (-webkit-min-device-pixel-ratio:0) { 10 | @font-face { 11 | font-family: 'meteocons'; 12 | src: url('../font/meteocons.svg?24967826#meteocons') format('svg'); 13 | } 14 | } 15 | */ 16 | 17 | [class^="meteocons-"]:before, [class*=" meteocons-"]:before { 18 | font-family: "meteocons"; 19 | font-style: normal; 20 | font-weight: normal; 21 | speak: none; 22 | 23 | display: inline-block; 24 | text-decoration: inherit; 25 | width: 1em; 26 | margin-right: .2em; 27 | text-align: center; 28 | /* opacity: .8; */ 29 | 30 | /* For safety - reset parent styles, that can break glyph codes*/ 31 | font-variant: normal; 32 | text-transform: none; 33 | 34 | /* fix buttons height, for twitter bootstrap */ 35 | line-height: 1em; 36 | 37 | /* Animation center compensation - margins should be symmetric */ 38 | /* remove if not needed */ 39 | margin-left: .2em; 40 | 41 | /* you can be more comfortable with increased icons size */ 42 | /* font-size: 120%; */ 43 | 44 | /* Uncomment for 3D effect */ 45 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 46 | } 47 | 48 | .meteocons-windy-rain-inv:before { content: '\e800'; } /* '' */ 49 | .meteocons-snow-inv:before { content: '\e801'; } /* '' */ 50 | .meteocons-snow-heavy-inv:before { content: '\e802'; } /* '' */ 51 | .meteocons-hail-inv:before { content: '\e803'; } /* '' */ 52 | .meteocons-clouds-inv:before { content: '\e804'; } /* '' */ 53 | .meteocons-clouds-flash-inv:before { content: '\e805'; } /* '' */ 54 | .meteocons-temperature:before { content: '\e806'; } /* '' */ 55 | .meteocons-compass:before { content: '\e807'; } /* '' */ 56 | .meteocons-na:before { content: '\e808'; } /* '' */ 57 | .meteocons-celcius:before { content: '\e809'; } /* '' */ 58 | .meteocons-fahrenheit:before { content: '\e80a'; } /* '' */ 59 | .meteocons-clouds-flash-alt:before { content: '\e80b'; } /* '' */ 60 | .meteocons-sun-inv:before { content: '\e80c'; } /* '' */ 61 | .meteocons-moon-inv:before { content: '\e80d'; } /* '' */ 62 | .meteocons-cloud-sun-inv:before { content: '\e80e'; } /* '' */ 63 | .meteocons-cloud-moon-inv:before { content: '\e80f'; } /* '' */ 64 | .meteocons-cloud-inv:before { content: '\e810'; } /* '' */ 65 | .meteocons-cloud-flash-inv:before { content: '\e811'; } /* '' */ 66 | .meteocons-drizzle-inv:before { content: '\e812'; } /* '' */ 67 | .meteocons-rain-inv:before { content: '\e813'; } /* '' */ 68 | .meteocons-windy-inv:before { content: '\e814'; } /* '' */ 69 | .meteocons-sunrise:before { content: '\e815'; } /* '' */ 70 | .meteocons-sun:before { content: '\e816'; } /* '' */ 71 | .meteocons-moon:before { content: '\e817'; } /* '' */ 72 | .meteocons-eclipse:before { content: '\e818'; } /* '' */ 73 | .meteocons-mist:before { content: '\e819'; } /* '' */ 74 | .meteocons-wind:before { content: '\e81a'; } /* '' */ 75 | .meteocons-snowflake:before { content: '\e81b'; } /* '' */ 76 | .meteocons-cloud-sun:before { content: '\e81c'; } /* '' */ 77 | .meteocons-cloud-moon:before { content: '\e81d'; } /* '' */ 78 | .meteocons-fog-sun:before { content: '\e81e'; } /* '' */ 79 | .meteocons-fog-moon:before { content: '\e81f'; } /* '' */ 80 | .meteocons-fog-cloud:before { content: '\e820'; } /* '' */ 81 | .meteocons-fog:before { content: '\e821'; } /* '' */ 82 | .meteocons-cloud:before { content: '\e822'; } /* '' */ 83 | .meteocons-cloud-flash:before { content: '\e823'; } /* '' */ 84 | .meteocons-cloud-flash-alt:before { content: '\e824'; } /* '' */ 85 | .meteocons-drizzle:before { content: '\e825'; } /* '' */ 86 | .meteocons-rain:before { content: '\e826'; } /* '' */ 87 | .meteocons-windy:before { content: '\e827'; } /* '' */ 88 | .meteocons-windy-rain:before { content: '\e828'; } /* '' */ 89 | .meteocons-snow:before { content: '\e829'; } /* '' */ 90 | .meteocons-snow-alt:before { content: '\e82a'; } /* '' */ 91 | .meteocons-snow-heavy:before { content: '\e82b'; } /* '' */ 92 | .meteocons-hail:before { content: '\e82c'; } /* '' */ 93 | .meteocons-clouds:before { content: '\e82d'; } /* '' */ 94 | .meteocons-clouds-flash:before { content: '\e82e'; } /* '' */ -------------------------------------------------------------------------------- /public/stylesheets/tooltipster-sideTip-punk.min.css: -------------------------------------------------------------------------------- 1 | .tooltipster-sidetip.tooltipster-punk .tooltipster-box{border-radius:5px;border:none;border-bottom:3px solid #f71169;background:#2a2a2a}.tooltipster-sidetip.tooltipster-punk.tooltipster-top .tooltipster-box{margin-bottom:7px}.tooltipster-sidetip.tooltipster-punk .tooltipster-content{color:#fff;padding:8px 16px}.tooltipster-sidetip.tooltipster-punk .tooltipster-arrow-background{display:none}.tooltipster-sidetip.tooltipster-punk.tooltipster-bottom .tooltipster-arrow-border{border-bottom-color:#2a2a2a}.tooltipster-sidetip.tooltipster-punk.tooltipster-left .tooltipster-arrow-border{border-left-color:#2a2a2a}.tooltipster-sidetip.tooltipster-punk.tooltipster-right .tooltipster-arrow-border{border-right-color:#2a2a2a}.tooltipster-sidetip.tooltipster-punk.tooltipster-top .tooltipster-arrow-border{border-top-color:#f71169} -------------------------------------------------------------------------------- /public/stylesheets/tooltipster.bundle.min.css: -------------------------------------------------------------------------------- 1 | .tooltipster-fall,.tooltipster-grow.tooltipster-show{-webkit-transition-timing-function:cubic-bezier(.175,.885,.32,1);-moz-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);-ms-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);-o-transition-timing-function:cubic-bezier(.175,.885,.32,1.15)}.tooltipster-base{display:flex;pointer-events:none;position:absolute}.tooltipster-box{flex:1 1 auto}.tooltipster-content{box-sizing:border-box;max-height:100%;max-width:100%;overflow:auto}.tooltipster-ruler{bottom:0;left:0;overflow:hidden;position:fixed;right:0;top:0;visibility:hidden}.tooltipster-fade{opacity:0;-webkit-transition-property:opacity;-moz-transition-property:opacity;-o-transition-property:opacity;-ms-transition-property:opacity;transition-property:opacity}.tooltipster-fade.tooltipster-show{opacity:1}.tooltipster-grow{-webkit-transform:scale(0,0);-moz-transform:scale(0,0);-o-transform:scale(0,0);-ms-transform:scale(0,0);transform:scale(0,0);-webkit-transition-property:-webkit-transform;-moz-transition-property:-moz-transform;-o-transition-property:-o-transform;-ms-transition-property:-ms-transform;transition-property:transform;-webkit-backface-visibility:hidden}.tooltipster-grow.tooltipster-show{-webkit-transform:scale(1,1);-moz-transform:scale(1,1);-o-transform:scale(1,1);-ms-transform:scale(1,1);transform:scale(1,1);-webkit-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);transition-timing-function:cubic-bezier(.175,.885,.32,1.15)}.tooltipster-swing{opacity:0;-webkit-transform:rotateZ(4deg);-moz-transform:rotateZ(4deg);-o-transform:rotateZ(4deg);-ms-transform:rotateZ(4deg);transform:rotateZ(4deg);-webkit-transition-property:-webkit-transform,opacity;-moz-transition-property:-moz-transform;-o-transition-property:-o-transform;-ms-transition-property:-ms-transform;transition-property:transform}.tooltipster-swing.tooltipster-show{opacity:1;-webkit-transform:rotateZ(0);-moz-transform:rotateZ(0);-o-transform:rotateZ(0);-ms-transform:rotateZ(0);transform:rotateZ(0);-webkit-transition-timing-function:cubic-bezier(.23,.635,.495,1);-webkit-transition-timing-function:cubic-bezier(.23,.635,.495,2.4);-moz-transition-timing-function:cubic-bezier(.23,.635,.495,2.4);-ms-transition-timing-function:cubic-bezier(.23,.635,.495,2.4);-o-transition-timing-function:cubic-bezier(.23,.635,.495,2.4);transition-timing-function:cubic-bezier(.23,.635,.495,2.4)}.tooltipster-fall{-webkit-transition-property:top;-moz-transition-property:top;-o-transition-property:top;-ms-transition-property:top;transition-property:top;-webkit-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);transition-timing-function:cubic-bezier(.175,.885,.32,1.15)}.tooltipster-fall.tooltipster-initial{top:0!important}.tooltipster-fall.tooltipster-dying{-webkit-transition-property:all;-moz-transition-property:all;-o-transition-property:all;-ms-transition-property:all;transition-property:all;top:0!important;opacity:0}.tooltipster-slide{-webkit-transition-property:left;-moz-transition-property:left;-o-transition-property:left;-ms-transition-property:left;transition-property:left;-webkit-transition-timing-function:cubic-bezier(.175,.885,.32,1);-webkit-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);-moz-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);-ms-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);-o-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);transition-timing-function:cubic-bezier(.175,.885,.32,1.15)}.tooltipster-slide.tooltipster-initial{left:-40px!important}.tooltipster-slide.tooltipster-dying{-webkit-transition-property:all;-moz-transition-property:all;-o-transition-property:all;-ms-transition-property:all;transition-property:all;left:0!important;opacity:0}@keyframes tooltipster-fading{0%{opacity:0}100%{opacity:1}}.tooltipster-update-fade{animation:tooltipster-fading .4s}@keyframes tooltipster-rotating{25%{transform:rotate(-2deg)}75%{transform:rotate(2deg)}100%{transform:rotate(0)}}.tooltipster-update-rotate{animation:tooltipster-rotating .6s}@keyframes tooltipster-scaling{50%{transform:scale(1.1)}100%{transform:scale(1)}}.tooltipster-update-scale{animation:tooltipster-scaling .6s}.tooltipster-sidetip .tooltipster-box{background:#565656;border:2px solid #000;border-radius:4px}.tooltipster-sidetip.tooltipster-bottom .tooltipster-box{margin-top:8px}.tooltipster-sidetip.tooltipster-left .tooltipster-box{margin-right:8px}.tooltipster-sidetip.tooltipster-right .tooltipster-box{margin-left:8px}.tooltipster-sidetip.tooltipster-top .tooltipster-box{margin-bottom:8px}.tooltipster-sidetip .tooltipster-content{color:#fff;line-height:18px;padding:6px 14px}.tooltipster-sidetip .tooltipster-arrow{overflow:hidden;position:absolute}.tooltipster-sidetip.tooltipster-bottom .tooltipster-arrow{height:10px;margin-left:-10px;top:0;width:20px}.tooltipster-sidetip.tooltipster-left .tooltipster-arrow{height:20px;margin-top:-10px;right:0;top:0;width:10px}.tooltipster-sidetip.tooltipster-right .tooltipster-arrow{height:20px;margin-top:-10px;left:0;top:0;width:10px}.tooltipster-sidetip.tooltipster-top .tooltipster-arrow{bottom:0;height:10px;margin-left:-10px;width:20px}.tooltipster-sidetip .tooltipster-arrow-background,.tooltipster-sidetip .tooltipster-arrow-border{height:0;position:absolute;width:0}.tooltipster-sidetip .tooltipster-arrow-background{border:10px solid transparent}.tooltipster-sidetip.tooltipster-bottom .tooltipster-arrow-background{border-bottom-color:#565656;left:0;top:3px}.tooltipster-sidetip.tooltipster-left .tooltipster-arrow-background{border-left-color:#565656;left:-3px;top:0}.tooltipster-sidetip.tooltipster-right .tooltipster-arrow-background{border-right-color:#565656;left:3px;top:0}.tooltipster-sidetip.tooltipster-top .tooltipster-arrow-background{border-top-color:#565656;left:0;top:-3px}.tooltipster-sidetip .tooltipster-arrow-border{border:10px solid transparent;left:0;top:0}.tooltipster-sidetip.tooltipster-bottom .tooltipster-arrow-border{border-bottom-color:#000}.tooltipster-sidetip.tooltipster-left .tooltipster-arrow-border{border-left-color:#000}.tooltipster-sidetip.tooltipster-right .tooltipster-arrow-border{border-right-color:#000}.tooltipster-sidetip.tooltipster-top .tooltipster-arrow-border{border-top-color:#000}.tooltipster-sidetip .tooltipster-arrow-uncropped{position:relative}.tooltipster-sidetip.tooltipster-bottom .tooltipster-arrow-uncropped{top:-10px}.tooltipster-sidetip.tooltipster-right .tooltipster-arrow-uncropped{left:-10px} -------------------------------------------------------------------------------- /spark-submit-ui.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/ApplicationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | import play.api.db.DB 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * Add your spec here. 10 | * You can mock out a whole application including requests, plugins etc. 11 | * For more information, consult the wiki. 12 | */ 13 | @RunWith(classOf[JUnitRunner]) 14 | class ApplicationSpec extends Specification { 15 | 16 | "Application" should { 17 | 18 | "send 404 on a bad request" in new WithApplication{ 19 | DB 20 | route(FakeRequest(GET, "/boum")) must beNone 21 | } 22 | 23 | "render the index page" in new WithApplication{ 24 | val home = route(FakeRequest(GET, "/")).get 25 | 26 | status(home) must equalTo(OK) 27 | contentType(home) must beSome.which(_ == "text/html") 28 | contentAsString(home) must contain ("Your new application is ready.") 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/IntegrationSpec.scala: -------------------------------------------------------------------------------- 1 | import org.specs2.mutable._ 2 | import org.specs2.runner._ 3 | import org.junit.runner._ 4 | 5 | import play.api.test._ 6 | import play.api.test.Helpers._ 7 | 8 | /** 9 | * add your integration spec here. 10 | * An integration test will fire up a whole play application in a real (or headless) browser 11 | */ 12 | @RunWith(classOf[JUnitRunner]) 13 | class IntegrationSpec extends Specification { 14 | 15 | "Application" should { 16 | 17 | "work from within a browser" in new WithBrowser { 18 | 19 | browser.goTo("http://localhost:" + port) 20 | 21 | browser.pageSource must contain("Your new application is ready.") 22 | } 23 | } 24 | } 25 | --------------------------------------------------------------------------------