/* * Copyright © 2014 Cask Data, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package co.cask.cdap; import co.cask.cdap.common.conf.CConfiguration; import co.cask.cdap.common.conf.Configuration; import co.cask.cdap.common.conf.SConfiguration; import co.cask.cdap.ui.ConfigurationJsonTool; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import com.google.common.io.Files; import com.google.common.util.concurrent.AbstractExecutionThreadService; import com.google.inject.Inject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Writer; import java.util.concurrent.Executor; import javax.annotation.Nullable; /** * UserInterfaceService is a basic Server wrapper that launches node.js and our * UI server.js file. It then basically sits there waiting, doing nothing. * * All output is sent to our Logging service. */ final class UserInterfaceService extends AbstractExecutionThreadService { private static final String JSON_PATH = "cdap-config.json"; private static final String JSON_SECURITY_PATH = "cdap-security-config.json"; private static final Logger LOG = LoggerFactory.getLogger(UserInterfaceService.class); private static final String NODE_JS_EXECUTABLE = "node"; private final CConfiguration cConf; private final SConfiguration sConf; private Process process; private BufferedReader bufferedReader; @Nullable private File cConfJsonFile; @Nullable private File sConfJsonFile; @Inject public UserInterfaceService(CConfiguration cConf, SConfiguration sConf) { this.cConf = cConf; this.sConf = sConf; } /** * Start the service. */ @Override protected void startUp() throws Exception { File confDir = new File("data", "conf"); if (!confDir.exists()) { Preconditions.checkState(confDir.mkdirs(), "Couldn't create directory for generated conf files for the UI"); } this.cConfJsonFile = new File(confDir, JSON_PATH); this.sConfJsonFile = new File(confDir, JSON_SECURITY_PATH); generateConfigFile(cConfJsonFile, cConf); generateConfigFile(sConfJsonFile, sConf); File uiPath = new File("cdap-ui", "server.js"); if (!uiPath.exists()) { uiPath = new File("ui", "server.js"); } Preconditions.checkState(uiPath.exists(), "Missing server.js at " + uiPath.getAbsolutePath()); ProcessBuilder builder = new ProcessBuilder(NODE_JS_EXECUTABLE, uiPath.getAbsolutePath(), "cConf=\"" + cConfJsonFile.getAbsolutePath() + "\"", "sConf=\"" + sConfJsonFile.getAbsolutePath() + "\""); builder.redirectErrorStream(true); LOG.info("Starting UI..."); process = builder.start(); final InputStream is = process.getInputStream(); final InputStreamReader isr = new InputStreamReader(is); bufferedReader = new BufferedReader(isr); } private void generateConfigFile(File path, Configuration config) throws Exception { try (Writer configWriter = Files.newWriter(path, Charsets.UTF_8)) { ConfigurationJsonTool.exportToJson(config, configWriter); } } /** * Processes the output of the command. */ @Override protected void run() throws Exception { LOG.info("UI running ..."); try { String line; while ((line = bufferedReader.readLine()) != null) { LOG.trace(line); } } catch (Exception e) { LOG.error(e.getMessage()); } } /** * Returns the {@link Executor} that will be used to run this service. */ @Override protected Executor executor() { return new Executor() { @Override public void execute(Runnable command) { Thread thread = new Thread(command, getServiceName()); thread.setDaemon(true); thread.start(); } }; } /** * Invoked to request the service to stop. * <p/> * <p>By default this method does nothing. */ @Override protected void triggerShutdown() { process.destroy(); } /** * Stop the service. */ @Override protected void shutDown() throws Exception { LOG.info("Shutting down UI ..."); process.waitFor(); // Cleanup generated files if (cConfJsonFile != null) { cConfJsonFile.delete(); } if (sConfJsonFile != null) { sConfJsonFile.delete(); } } }