@Test(timeout = 10000)
  public void testFailAbortDoesntHang() throws IOException {
    Configuration conf = new Configuration();
    conf.set(MRJobConfig.MR_AM_STAGING_DIR, stagingDir);
    conf.set(MRJobConfig.MR_AM_COMMITTER_CANCEL_TIMEOUT_MS, "1000");

    DrainDispatcher dispatcher = new DrainDispatcher();
    dispatcher.init(conf);
    dispatcher.start();
    OutputCommitter committer = Mockito.mock(OutputCommitter.class);
    CommitterEventHandler commitHandler = createCommitterEventHandler(dispatcher, committer);
    commitHandler.init(conf);
    commitHandler.start();
    // Job has only 1 mapper task. No reducers
    conf.setInt(MRJobConfig.NUM_REDUCES, 0);
    conf.setInt(MRJobConfig.MAP_MAX_ATTEMPTS, 1);
    JobImpl job = createRunningStubbedJob(conf, dispatcher, 1, null);

    // Fail / finish all the tasks. This should land the JobImpl directly in the
    // FAIL_ABORT state
    for (Task t : job.tasks.values()) {
      TaskImpl task = (TaskImpl) t;
      task.handle(new TaskEvent(task.getID(), TaskEventType.T_SCHEDULE));
      for (TaskAttempt ta : task.getAttempts().values()) {
        task.handle(new TaskTAttemptEvent(ta.getID(), TaskEventType.T_ATTEMPT_FAILED));
      }
    }
    assertJobState(job, JobStateInternal.FAIL_ABORT);

    dispatcher.await();
    // Verify abortJob is called once and the job failed
    Mockito.verify(committer, Mockito.timeout(2000).times(1))
        .abortJob((JobContext) Mockito.any(), (State) Mockito.any());
    assertJobState(job, JobStateInternal.FAILED);

    dispatcher.stop();
  }
  public Task toEntityModel() {
    TaskImpl taskImpl = new TaskImpl();

    taskImpl.setTaskId(taskId);
    taskImpl.setCompanyId(companyId);

    if (createDate == Long.MIN_VALUE) {
      taskImpl.setCreateDate(null);
    } else {
      taskImpl.setCreateDate(new Date(createDate));
    }

    taskImpl.setCreatorId(creatorId);

    if (taskName == null) {
      taskImpl.setTaskName(StringPool.BLANK);
    } else {
      taskImpl.setTaskName(taskName);
    }

    taskImpl.resetOriginalValues();

    return taskImpl;
  }
  private static int[] spanFor(
      CompilationInfo info, JavaSource js, final PackageMemberAnnotation annotation) {
    final int[] result = new int[] {-1, -1};
    class TaskImpl implements Task<CompilationInfo> {
      @Override
      public void run(final CompilationInfo parameter) {
        TypeElement clazz = parameter.getElements().getTypeElement(annotation.getClassName());

        if (clazz == null) {
          // XXX: log
          return;
        }

        Element resolved = null;

        if (annotation instanceof FieldAnnotation) {
          FieldAnnotation fa = (FieldAnnotation) annotation;

          for (VariableElement var : ElementFilter.fieldsIn(clazz.getEnclosedElements())) {
            if (var.getSimpleName().contentEquals(fa.getFieldName())) {
              resolved = var;
              break;
            }
          }
        } else if (annotation instanceof MethodAnnotation) {
          MethodAnnotation ma = (MethodAnnotation) annotation;

          for (ExecutableElement method : ElementFilter.methodsIn(clazz.getEnclosedElements())) {
            if (method.getSimpleName().contentEquals(ma.getMethodName())) {
              if (ma.getMethodSignature()
                  .equals(SourceUtils.getJVMSignature(ElementHandle.create(method))[2])) {
                resolved = method;
                break;
              }
            }
          }
        } else if (annotation instanceof ClassAnnotation) {
          resolved = clazz;
        }

        if (resolved == null) {
          // XXX: log
          return;
        }

        final Element resolvedFin = resolved;

        new CancellableTreePathScanner<Void, Void>() {
          @Override
          public Void visitVariable(VariableTree node, Void p) {
            if (resolvedFin.equals(parameter.getTrees().getElement(getCurrentPath()))) {
              int[] span = parameter.getTreeUtilities().findNameSpan(node);

              if (span != null) {
                result[0] = span[0];
                result[1] = span[1];
              }
            }

            return super.visitVariable(node, p);
          }

          @Override
          public Void visitMethod(MethodTree node, Void p) {
            if (resolvedFin.equals(parameter.getTrees().getElement(getCurrentPath()))) {
              int[] span = parameter.getTreeUtilities().findNameSpan(node);

              if (span != null) {
                result[0] = span[0];
                result[1] = span[1];
              }
            }

            return super.visitMethod(node, p);
          }

          @Override
          public Void visitClass(ClassTree node, Void p) {
            if (resolvedFin.equals(parameter.getTrees().getElement(getCurrentPath()))) {
              int[] span = parameter.getTreeUtilities().findNameSpan(node);

              if (span != null) {
                result[0] = span[0];
                result[1] = span[1];
              }
            }

            return super.visitClass(node, p);
          }
        }.scan(parameter.getCompilationUnit(), null);
      }
    };

    final TaskImpl convertor = new TaskImpl();

    if (info != null) {
      convertor.run(info);
    } else {
      try {
        js.runUserActionTask(
            new Task<CompilationController>() {
              @Override
              public void run(CompilationController parameter) throws Exception {
                parameter.toPhase(
                    Phase.RESOLVED); // XXX: ENTER should be enough in most cases, but not for
                // anonymous innerclasses.
                convertor.run(parameter);
              }
            },
            true);
      } catch (IOException ex) {
        Exceptions.printStackTrace(ex);
      }
    }

    return result;
  }