| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.gearpump.cluster.main |
| |
| import java.io.File |
| import java.net.{URL, URLClassLoader} |
| import java.util.jar.JarFile |
| |
| import org.apache.gearpump.cluster.client.RuntimeEnvironment |
| import org.apache.gearpump.util.{Constants, LogUtil, MasterClientCommand, Util} |
| |
| import scala.util.{Failure, Success, Try} |
| |
| /** Tool to submit an application jar to cluster */ |
| object AppSubmitter extends MasterClientCommand with ArgumentsParser { |
| |
| override val ignoreUnknownArgument = true |
| |
| override val description = "Submit an application to Master by providing a jar" |
| |
| override val options: Array[(String, CLIOption[Any])] = Array( |
| "namePrefix" -> CLIOption[String]("<application name prefix>", required = false, |
| defaultValue = Some("")), |
| "jar" -> CLIOption("<application>.jar", required = true), |
| "executors" -> CLIOption[Int]("number of executor to launch", required = false, |
| defaultValue = None), |
| "verbose" -> CLIOption("<print verbose log on console>", required = false, |
| defaultValue = Some(false)), |
| // For document purpose only, OPTION_CONFIG option is not used here. |
| // OPTION_CONFIG is parsed by parent shell command "Gear" transparently. |
| Gear.OPTION_CONFIG -> CLIOption("custom configuration file", required = false, |
| defaultValue = None)) |
| |
| def main(akkaConf: Config, args: Array[String]): Unit = { |
| |
| val config = parse(args) |
| if (null != config) { |
| |
| val verbose = config.getBoolean("verbose") |
| if (verbose) { |
| LogUtil.verboseLogToConsole() |
| } |
| |
| val jar = config.getString("jar") |
| |
| // Set jar path to be submitted to cluster |
| System.setProperty(Constants.GEARPUMP_APP_JAR, jar) |
| if (config.exists("executors")) { |
| System.setProperty(Constants.APPLICATION_EXECUTOR_NUMBER, |
| config.getInt("executors").toString) |
| } |
| |
| val namePrefix = config.getString("namePrefix") |
| if (namePrefix.nonEmpty) { |
| if (!Util.validApplicationName(namePrefix)) { |
| throw new Exception(s"$namePrefix is not a valid prefix for an application name") |
| } |
| System.setProperty(Constants.GEARPUMP_APP_NAME_PREFIX, namePrefix) |
| } |
| |
| val jarFile = new java.io.File(jar) |
| |
| // Start main class |
| if (!jarFile.exists()) { |
| throw new Exception(s"jar $jar does not exist") |
| } |
| |
| val classLoader: URLClassLoader = new URLClassLoader(Array(new URL("file:" + |
| jarFile.getAbsolutePath)), Thread.currentThread().getContextClassLoader) |
| val (main, arguments) = parseMain(jarFile, config.remainArgs, classLoader) |
| |
| // Set to context classLoader. ActorSystem pick context classLoader in preference |
| Thread.currentThread().setContextClassLoader(classLoader) |
| val clazz = classLoader.loadClass(main) |
| val mainMethod = clazz.getMethod("main", classOf[Array[String]]) |
| mainMethod.invoke(null, arguments) |
| } |
| } |
| |
| private def parseMain(jar: File, remainArgs: Array[String], |
| classLoader: ClassLoader): (String, Array[String]) = { |
| if (remainArgs.nonEmpty && Try(classLoader.loadClass(remainArgs.head)).isSuccess) { |
| (remainArgs.head, remainArgs.tail) |
| } else { |
| val mainInManifest = |
| new JarFile(jar).getManifest.getMainAttributes.getValue("Main-Class") |
| val args = remainArgs.mkString(" ") |
| Try(classLoader.loadClass(mainInManifest)) match { |
| case Success(_) => |
| // scalastyle:off println |
| Console.println( |
| s"""Can't load main class in arguments $args |
| |Loading $mainInManifest in manifest""".stripMargin) |
| // scalastyle:on println |
| (mainInManifest, remainArgs) |
| case Failure(_) => |
| throw new IllegalArgumentException( |
| s"Can't load main class in arguments $args or $mainInManifest in manifest") |
| } |
| } |
| } |
| } |