/**
* Licensed to Ravel, Inc. under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Ravel, Inc. 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.goldenorb;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.hadoop.fs.Path;
import org.apache.zookeeper.ZooKeeper;
import org.goldenorb.conf.OrbConfiguration;
import org.goldenorb.util.StreamWriter;
import org.goldenorb.zookeeper.OrbZKFailure;
import org.goldenorb.zookeeper.ZookeeperUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link OrbPartitionProcess} is the {@link OrbPartition} process launcher. It sets the command-line
* arguments and output/error streams for each process.
*/
public class OrbPartitionProcess implements PartitionProcess {
private Process process;
private OrbConfiguration conf;
private OrbConfiguration jobConf;
private int processNum;
private boolean reserved = false;
private int partitionID;
private String jobNumber;
private final Logger logger = LoggerFactory.getLogger(OrbPartitionProcess.class);
/**
* Constructor
*
*/
public OrbPartitionProcess() {}
/**
* Constructor
*
* @param OrbConfiguration
* conf
* @param int processNum
* @param boolean reserved
* @param int partitionID
*/
public OrbPartitionProcess(OrbConfiguration conf, OrbConfiguration jobConf, int processNum, boolean reserved, int partitionID) {
this.conf = conf;
this.processNum = processNum;
this.reserved = reserved;
this.partitionID = partitionID;
this.jobConf = jobConf;
}
/**
*
* @param FileOutputStream
* outStream
* @param FileOutputStream
* errStream
*/
@Override
public void launch(OutputStream outStream, OutputStream errStream) {
try {
String customClassPath = buildClassPathPart();
int orbBasePort = conf.getOrbBasePort();
String tmp = jobConf.getOrbPartitionJavaopts();
String[] orbPartitionJavaopts = tmp != null ? tmp.split(" ") : new String[0];
String sysClassPath = System.getProperties().getProperty("java.class.path", null);
String debugOpts = jobConf.getPartitionDebug() ? setupDebugOptions() : new String();
List<String> args = new ArrayList<String>();
args.add(System.getProperty("java.home") + "/bin/java");
if(debugOpts.length() > 0) {
args.add(setupDebugOptions());
}
args.addAll(Arrays.asList(orbPartitionJavaopts));
args.add("-cp");
args.add(sysClassPath + customClassPath);
args.add("org.goldenorb.OrbPartition");
args.add(jobNumber);
args.add(Integer.toString(partitionID));
args.add(Boolean.toString(reserved));
args.add(Integer.toString(orbBasePort + processNum));
logger.debug("process args: {}", args.toString());
ProcessBuilder builder = new ProcessBuilder();
builder.command(args);
builder.environment().clear();
process = builder.start();
// Runtime runtime = Runtime.getRuntime();
// process = runtime.exec(args.toArray(new String[args.size()]), new String[0]);
new StreamWriter(process.getErrorStream(), errStream);
new StreamWriter(process.getInputStream(), outStream);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (OrbZKFailure e) {
logger.error("Exception occured while adding distributed files to the class path");
e.printStackTrace();
}
}
/**
*
* @throws InterruptedException
* @throws IOException
* @throws OrbZKFailure
* @returns String
*/
private String buildClassPathPart() throws IOException, InterruptedException, OrbZKFailure {
StringBuilder sb = new StringBuilder();
String[] orbClassPaths = conf.getOrbClassPaths();
if (orbClassPaths != null) {
for (String cp : orbClassPaths) {
sb.append(":");
sb.append(cp);
}
}
List<String> localFilesPath = getLocalFilesPath();
if (localFilesPath != null) {
for (String cp : localFilesPath) {
sb.append(":");
sb.append(cp);
}
}
return sb.toString();
}
/**
* Calculates what the class paths for the distributed files that are in this partitions temp directory. All
* files are distributed to /temp directory/GoldenOrb/jobNumber/file name.
*
* @return A list of the class paths that need to be added to this partitions class path
* @throws IOException
* @throws InterruptedException
* @throws OrbZKFailure
*/
private List<String> getLocalFilesPath() throws IOException, InterruptedException, OrbZKFailure {
List<String> paths = null;
ZooKeeper zk = ZookeeperUtils.connect(conf.getOrbZooKeeperQuorum());
String zkPath = "/GoldenOrb/" + conf.getOrbClusterName() + "/JobQueue/" + jobNumber;
logger.info("Getting node " + zkPath + " from ZooKeeper");
OrbConfiguration orbConf = (OrbConfiguration) ZookeeperUtils.getNodeWritable(zk, zkPath,
OrbConfiguration.class, conf);
if (orbConf != null) {
Path[] tempPaths = orbConf.getHDFSdistributedFiles();
if (tempPaths != null) {
paths = new ArrayList<String>();
for (Path path : tempPaths) {
String[] name = path.toString().split("/");
// files are always put in /<temp directory>/GoldenOrb/<jobNumber>/<file name>
paths.add(System.getProperty("java.io.tmpdir") + "/GoldenOrb/" + orbConf.getOrbClusterName() + "/"
+ jobNumber + "/" + name[name.length - 1]);
}
}
}
zk.close();
return paths;
}
/**
*
*/
@Override
public void kill() {
process.destroy();
}
/**
* Return the conf
*/
public OrbConfiguration getConf() {
return conf;
}
/**
* Set the conf
*
* @param OrbConfiguration
* conf
*/
@Override
public void setConf(OrbConfiguration conf) {
this.conf = conf;
}
/**
* Return the processNum
*/
@Override
public int getProcessNum() {
return processNum;
}
/**
* Set the processNum
*
* @param int processNum
*/
@Override
public void setProcessNum(int processNum) {
this.processNum = processNum;
}
/**
* Return the unning
*/
@Override
public boolean isRunning() {
boolean ret = false;
try {
process.exitValue();
} catch (IllegalThreadStateException e) {
e.printStackTrace();
ret = true;
}
return ret;
}
/**
* Set the reserved
*
* @param boolean reserved
*/
@Override
public void setReserved(boolean reserved) {
this.reserved = reserved;
}
/**
* Return the eserved
*/
@Override
public boolean isReserved() {
return reserved;
}
/**
* Set the partitionID
*
* @param int partitionID
*/
@Override
public void setPartitionID(int partitionID) {
this.partitionID = partitionID;
}
/**
* Return the partitionID
*/
@Override
public int getPartitionID() {
return partitionID;
}
@Override
public void setJobNumber(String jobNumber) {
this.jobNumber = jobNumber;
}
@Override
public String getJobNumber() {
return jobNumber;
}
private String setupDebugOptions() {
StringBuilder sb = new StringBuilder();
String ids = jobConf.getPartitionDebugIds();
boolean debugThisPartition = false;
if(ids.equalsIgnoreCase("*")) {
debugThisPartition = true;
}
else {
for(String id : ids.split(",")) {
if(Integer.parseInt(id) == partitionID) {
debugThisPartition = true;
break;
}
}
}
if(debugThisPartition) {
int debugPort = jobConf.getPartitionDebugBaseport() + partitionID;
sb.append("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=localhost:");
sb.append(debugPort);
}
return sb.toString();
}
@Override
public void setCurrentJobConf(OrbConfiguration jobConf) {
this.jobConf = jobConf;
}
@Override
public OrbConfiguration getCurrentJobConf() {
return jobConf;
}
}