| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| using System; |
| using System.Collections.Generic; |
| using System.Diagnostics; |
| using System.Globalization; |
| using System.IO; |
| using System.Linq; |
| using System.Threading.Tasks; |
| using Org.Apache.REEF.Client.API.Exceptions; |
| using Org.Apache.REEF.Tang.Annotations; |
| using Org.Apache.REEF.Utilities.Diagnostics; |
| using Org.Apache.REEF.Utilities.Logging; |
| |
| namespace Org.Apache.REEF.Client.Common |
| { |
| /// <summary> |
| /// Helper class to launch the java side of the various clients. |
| /// </summary> |
| internal class JavaClientLauncher : IJavaClientLauncher |
| { |
| /// <summary> |
| /// The folder in which we search for the client jar. |
| /// </summary> |
| private const string JarFolder = "./"; |
| |
| private static readonly Logger Logger = Logger.GetLogger(typeof(JavaClientLauncher)); |
| private readonly IList<string> _additionalClasspathEntries = new List<string>(); |
| |
| [Inject] |
| private JavaClientLauncher() |
| { |
| } |
| |
| /// <summary> |
| /// Launch a java class in ClientConstants.ClientJarFilePrefix with provided parameters. |
| /// </summary> |
| /// <param name="javaLogLevel"></param> |
| /// <param name="javaClassName"></param> |
| /// <param name="parameters"></param> |
| public Task LaunchAsync(JavaLoggingSetting javaLogLevel, string javaClassName, params string[] parameters) |
| { |
| var startInfo = new ProcessStartInfo |
| { |
| Arguments = AssembleArguments(javaLogLevel, javaClassName, parameters), |
| FileName = GetJavaCommand(), |
| UseShellExecute = false, |
| RedirectStandardOutput = true, |
| RedirectStandardError = true, |
| }; |
| |
| var msg = string.Format(CultureInfo.CurrentCulture, "Launch Java with command: {0} {1}", |
| startInfo.FileName, startInfo.Arguments); |
| Logger.Log(Level.Info, msg); |
| |
| var process = Process.Start(startInfo); |
| var processExitTracker = new TaskCompletionSource<bool>(); |
| if (process != null) |
| { |
| process.EnableRaisingEvents = true; |
| process.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e) |
| { |
| if (!string.IsNullOrWhiteSpace(e.Data)) |
| { |
| Logger.Log(Level.Info, e.Data); |
| } |
| }; |
| process.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs e) |
| { |
| if (!string.IsNullOrWhiteSpace(e.Data)) |
| { |
| Logger.Log(Level.Error, e.Data); |
| } |
| }; |
| process.BeginErrorReadLine(); |
| process.BeginOutputReadLine(); |
| process.Exited += (sender, args) => { processExitTracker.SetResult(process.ExitCode == 0); }; |
| } |
| else |
| { |
| processExitTracker.SetException(new Exception("Java client process didn't start.")); |
| } |
| |
| return processExitTracker.Task; |
| } |
| |
| /// <summary> |
| /// Assembles the command line arguments. Used by LaunchAsync() |
| /// </summary> |
| /// <param name="javaLogLevel"></param> |
| /// <param name="javaClassName"></param> |
| /// <param name="parameters"></param> |
| /// <returns></returns> |
| private string AssembleArguments(JavaLoggingSetting javaLogLevel, string javaClassName, params string[] parameters) |
| { |
| IList<string> arguments = new List<string>(); |
| |
| if (javaLogLevel == JavaLoggingSetting.Verbose) |
| { |
| arguments.Add("-Djava.util.logging.config.class=org.apache.reef.util.logging.Config"); |
| } |
| |
| arguments.Add("-cp"); |
| arguments.Add(GetClientClasspath()); |
| arguments.Add(javaClassName); |
| foreach (var parameter in parameters) |
| { |
| arguments.Add(parameter); |
| } |
| return string.Join(" ", arguments); |
| } |
| |
| /// <summary> |
| /// Find the `java` command on this machine and returns its path. |
| /// </summary> |
| /// <exception cref="JavaNotFoundException">If the java binary couldn't be found.</exception> |
| /// <returns>The path of the `java` command on this machine.</returns> |
| private static string GetJavaCommand() |
| { |
| var javaHomePath = Environment.GetEnvironmentVariable("JAVA_HOME"); |
| if (string.IsNullOrWhiteSpace(javaHomePath)) |
| { |
| // TODO: Attempt to find java via the registry. |
| Exceptions.Throw( |
| new JavaNotFoundException("JAVA_HOME isn't set. Please install Java and make set JAVA_HOME"), Logger); |
| } |
| |
| if (!Directory.Exists(javaHomePath)) |
| { |
| Exceptions.Throw( |
| new JavaNotFoundException("JAVA_HOME references a folder that doesn't exist.", javaHomePath), Logger); |
| } |
| |
| var javaBinPath = Path.Combine(javaHomePath, "bin"); |
| if (!Directory.Exists(javaBinPath)) |
| { |
| throw new JavaNotFoundException( |
| "JAVA_HOME references a folder that doesn't contain a `bin` folder. Please adjust JAVA_HOME", |
| javaHomePath); |
| } |
| |
| var javaPath = Path.Combine(javaBinPath, "java.exe"); |
| if (!File.Exists(javaPath)) |
| { |
| Exceptions.Throw( |
| new JavaNotFoundException( |
| "Could not find java.exe on this machine. Is Java installed and JAVA_HOME set?", javaPath), |
| Logger); |
| } |
| return javaPath; |
| } |
| |
| /// <summary> |
| /// Assembles the classpath for the side process |
| /// </summary> |
| /// <exception cref="ClasspathException">If the classpath would be empty.</exception> |
| /// <returns></returns> |
| private string GetClientClasspath() |
| { |
| var files = Directory.GetFiles(JarFolder) |
| .Where(x => (!string.IsNullOrWhiteSpace(x))) |
| .Where(e => Path.GetFileName(e).ToLower().StartsWith(ClientConstants.ClientJarFilePrefix)) |
| .ToList(); |
| |
| if (files.Count == 0) |
| { |
| Exceptions.Throw(new ClasspathException( |
| "Unable to assemble classpath. Make sure the REEF JAR is in the current working directory."), Logger); |
| } |
| |
| var classpathEntries = new List<string>(_additionalClasspathEntries).Concat(files); |
| return string.Join(";", classpathEntries); |
| } |
| |
| /// <summary> |
| /// Add entries to the end of the classpath of the java client. |
| /// </summary> |
| /// <param name="entries"></param> |
| public void AddToClassPath(IEnumerable<string> entries) |
| { |
| foreach (var entry in entries) |
| { |
| _additionalClasspathEntries.Add(entry); |
| } |
| } |
| } |
| } |