/** {@inheritDoc} */
  @Override
  protected void configureJob(final Job job) throws IOException {
    if (null == mScoreFunctionClass) {
      throw new JobConfigurationException("Must specify a ScoreFunction class.");
    }
    if (null == mClientDataRequest) {
      mClientDataRequest = DEFAULT_CLIENT_REQUEST;
    }
    if (null == mAttachedColumn) {
      throw new JobConfigurationException("Must specified an AttachedColumn.");
    }
    if (null == mParameters) {
      mParameters = DEFAULT_PARAMETERS;
    }

    final Configuration conf = job.getConfiguration();
    conf.setClass(SCORE_FUNCTION_CLASS_CONF_KEY, mScoreFunctionClass, ScoreFunction.class);
    if (!getInputTableURI().equals(mJobOutput.getOutputTableURI())) {
      throw new JobConfigurationException(
          String.format(
              "Output table must be the same as the input" + "table. Got input: %s output: %s",
              getInputTableURI(), mJobOutput.getOutputTableURI()));
    }
    conf.set(SCORE_FUNCTION_ATTACHED_COLUMN_CONF_KEY, mAttachedColumn.getName());
    conf.set(SCORE_FUNCTION_PARAMETERS_CONF_KEY, GSON.toJson(mParameters, Map.class));
    conf.set(
        SCORE_FUNCTION_CLIENT_DATA_REQUEST_CONF_KEY,
        Base64.encodeBase64String(SerializationUtils.serialize(mClientDataRequest)));
    mMapper = new ScoreFunctionMapper();
    mReducer = new IdentityReducer<Object, Object>();
    job.setJobName("Kiji ScoreFunction: " + mScoreFunctionClass.getSimpleName());
    mScoreFunction = ReflectionUtils.newInstance(mScoreFunctionClass, conf);
    final FreshenerContext context =
        InternalFreshenerContext.create(
            mClientDataRequest,
            mAttachedColumn,
            mParameters,
            Maps.<String, String>newHashMap(),
            KeyValueStoreReaderFactory.create(getRequiredStores()));
    mScoreFunctionDataRequest = mScoreFunction.getDataRequest(context);

    super.configureJob(job);
  }
 @Override
 public void close() throws IOException {
   mKVStoreFactory.close();
 }
 @Override
 public <K, V> KeyValueStoreReader<K, V> getStore(String storeName) throws IOException {
   return mKVStoreFactory.openStore(storeName);
 }
 /**
  * Constructs a new KijiWebContext given the bound KV stores and the output column to "write" the
  * final results to upon completion of the producer.
  *
  * @param boundStores is the map of name to KVStore.
  * @param outputColumn is the name of the column to write the results to.
  */
 public KijiWebContext(Map<String, KeyValueStore<?, ?>> boundStores, KijiColumnName outputColumn) {
   mKVStoreFactory = KeyValueStoreReaderFactory.create(boundStores);
   mFamily = outputColumn.getFamily();
   mQualifier = outputColumn.getQualifier();
 }