/**
* (c) Copyright 2012 WibiData, Inc.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* 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 org.kiji.mapreduce.tools;
import java.io.IOException;
import java.util.List;
import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.kiji.annotations.ApiAudience;
import org.kiji.common.flags.Flag;
import org.kiji.mapreduce.output.DirectKijiTableMapReduceJobOutput;
import org.kiji.mapreduce.output.HFileMapReduceJobOutput;
import org.kiji.mapreduce.output.KijiTableMapReduceJobOutput;
import org.kiji.mapreduce.produce.KijiProduceJobBuilder;
import org.kiji.mapreduce.produce.impl.KijiProducers;
import org.kiji.mapreduce.tools.framework.KijiJobTool;
import org.kiji.schema.tools.KijiToolLauncher;
/** Program for running a Kiji producer in a MapReduce job. */
@ApiAudience.Private
public final class KijiProduce extends KijiJobTool<KijiProduceJobBuilder> {
private static final Logger LOG = LoggerFactory.getLogger(KijiProduce.class);
@Flag(name = "producer", usage = "Fully-qualified class name of the producer to run")
private String mProducerName = "";
@Flag(name = "num-threads", usage = "Positive integer number of threads to use")
private int mNumThreadsPerMapper = 1;
/** Producer must output to a Kiji table, and the output table must be the input table. */
private KijiTableMapReduceJobOutput mOutput;
/** {@inheritDoc} */
@Override
public String getName() {
return "produce";
}
/** {@inheritDoc} */
@Override
public String getDescription() {
return "Run a KijiProducer over a table";
}
/** {@inheritDoc} */
@Override
public String getCategory() {
return "MapReduce";
}
@Override
protected void validateFlags() throws Exception {
// Parse --input and --output flags, --input is guaranteed to be a Kiji table:
super.validateFlags();
Preconditions.checkArgument(!mProducerName.isEmpty(),
"Specify a producer with --producer=...");
Preconditions.checkArgument(mNumThreadsPerMapper >= 1,
"Illegal number of threads per mapper: {}, must", mNumThreadsPerMapper);
Preconditions.checkArgument(getJobOutput() instanceof KijiTableMapReduceJobOutput,
"Producer must output to a Kiji table but got {}.", getJobOutput().getClass().getName());
mOutput = (KijiTableMapReduceJobOutput) getJobOutput();
Preconditions.checkArgument(
mOutput.getOutputTableURI().equals(getJobInputTable().getInputTableURI()),
"Producer job output table {} does not match input table {}",
mOutput.getOutputTableURI(), getJobInputTable().getInputTableURI());
}
/** {@inheritDoc} */
@Override
protected KijiProduceJobBuilder createJobBuilder() {
return KijiProduceJobBuilder.create();
}
/** {@inheritDoc} */
@Override
protected void configure(KijiProduceJobBuilder jobBuilder) throws ClassNotFoundException,
IOException {
// Configure job input:
super.configure(jobBuilder);
jobBuilder
.withProducer(KijiProducers.forName(mProducerName))
.withOutput(mOutput)
.withNumThreads(mNumThreadsPerMapper);
}
/** {@inheritDoc} */
@Override
protected int run(List<String> nonFlagArgs) throws Exception {
final int jobStatus = super.run(nonFlagArgs);
// TODO: Move this to job outputs?
if (mOutput instanceof DirectKijiTableMapReduceJobOutput) {
if (jobStatus == 0) {
LOG.info("Producer {} for table {} completed successfully.",
mProducerName, mOutput.getOutputTableURI());
} else {
LOG.error("Producer {} failed: table {} may have partial writes.",
mProducerName, mOutput.getOutputTableURI());
}
} else if (mOutput instanceof HFileMapReduceJobOutput) {
if (jobStatus == 0) {
LOG.info("Producer {} for table {} completed successfully and wrote HFiles. "
+ "HFiles may now be loaded with: {}",
mProducerName, mOutput.getOutputTableURI(),
String.format("kiji bulk-load --table=%s", mOutput.getOutputTableURI()));
} else {
LOG.error("Producer {} for table {} failed: HFiles were not generated properly.",
mProducerName, mOutput.getOutputTableURI());
}
} else {
LOG.error("Unknown job output format: {}", mOutput.getClass().getName());
}
return jobStatus;
}
/**
* Program entry point.
*
* @param args The command-line arguments.
* @throws Exception If there is an error.
*/
public static void main(String[] args) throws Exception {
System.exit(new KijiToolLauncher().run(new KijiProduce(), args));
}
}