/** * (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.framework; import java.io.IOException; import java.util.List; import com.google.common.base.Preconditions; import org.kiji.annotations.ApiAudience; import org.kiji.annotations.ApiStability; import org.kiji.annotations.Inheritance; import org.kiji.common.flags.Flag; import org.kiji.mapreduce.MapReduceJobInput; import org.kiji.mapreduce.MapReduceJobOutput; import org.kiji.mapreduce.framework.MapReduceJobBuilder; import org.kiji.schema.platform.SchemaPlatformBridge; import org.kiji.schema.tools.BaseTool; /** * Base class for tools that run MapReduce jobs. * * @param <B> The type of job builder to use. */ @ApiAudience.Framework @ApiStability.Evolving @Inheritance.Extensible public abstract class JobTool<B extends MapReduceJobBuilder> extends BaseTool { // TODO(KIJIMR-62): Better usage doc: @Flag(name="input", usage="Job input specification: --input=\"format=<input-format> ...\".") protected String mInputFlag = ""; @Flag(name="output", usage="Job output specification: --output=\"format=<output-format> nsplits=N ...\"") protected String mOutputFlag = ""; @Flag(name="lib", usage="A directory of jars for including user code.\n" + "\tUnqualified paths are resolved in the local filesystem.") protected String mLibDir = ""; @Flag(name="kvstores", usage="KeyValueStore specification XML file to attach to the job") protected String mKvStoreFile = ""; private MapReduceJobInput mJobInput; private MapReduceJobOutput mJobOutput; /** {@inheritDoc} */ @Override protected void validateFlags() throws Exception { Preconditions.checkArgument(!mInputFlag.isEmpty(), "Specify an input to the job with --input=..."); Preconditions.checkArgument(!mOutputFlag.isEmpty(), "Specify an output to the job with --output=..."); mJobInput = MapReduceJobInputFactory.create().fromSpaceSeparatedMap(mInputFlag); mJobOutput = MapReduceJobOutputFactory.create().fromSpaceSeparatedMap(mOutputFlag); } /** * Creates a new job builder. * * @return A new job builder. */ protected abstract B createJobBuilder(); /** * Configures the job builder by applying the flags from the user. * * @param jobBuilder The job builder to configure. * @throws ClassNotFoundException If a mapper, reducer, or other user class can not be loaded. * @throws IOException if there is an error reading configuration input data. * @throws JobIOSpecParseException If the input or output for the job can not configured. */ protected void configure(B jobBuilder) throws ClassNotFoundException, IOException { // Get Hadoop config files (hdfs-site.xml, etc) into Configuration objects. // The Configuration for this instance will also get the new default values as // an underlay. SchemaPlatformBridge.get().initializeHadoopResources(); // Use default environment configuration: jobBuilder.withConf(getConf()); // Add user dependency jars if specified. if (!mLibDir.isEmpty()) { jobBuilder.addJarDirectory(mLibDir); } // Add user-specified KVStore definitions, if specified. if (!mKvStoreFile.isEmpty()) { jobBuilder.withStoreBindingsFile(mKvStoreFile); } } /** {@inheritDoc} */ @Override protected int run(List<String> nonFlagArgs) throws Exception { final B jobBuilder = createJobBuilder(); Preconditions.checkNotNull(jobBuilder, "Internal error: unable to create job builder?"); configure(jobBuilder); try { final boolean success = jobBuilder.build().run(); if (success) { getPrintStream().println("Job successful"); } else { getPrintStream().println("Job failed!"); } return success ? 0 : 1; } catch (RuntimeException exception) { getPrintStream().println("\nJob failed with exception: " + exception.getMessage()); throw exception; } } /** @return the job input. */ protected final MapReduceJobInput getJobInput() { return mJobInput; } /** @return the job output. */ protected final MapReduceJobOutput getJobOutput() { return mJobOutput; } }