/** {@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);
  }