public synchronized void collectionProcessComplete() {

    if (entityCount == 0) {
      LOG.info("[{}] collectionProcessComplete, processed 0 cas", getName());
      // System.exit(0); // otherwise it hangs, somehow...
      return;
    }

    final long duration = currentTimeMillis() - start;
    LOG.info(
        "[{}] collectionProcessComplete, processed {} cas in {}s (avrg. {}ms per CAS)",
        new Object[] {getName(), entityCount, duration / 1000 + "", duration / entityCount});
    this.collectionProcessComplete = true;

    // print performance stats
    StringBuffer buf = new StringBuffer();
    ProcessTrace processTrace = cpe.getPerformanceReport();
    // count total time so we can do percentages
    int totalTime = 0;
    for (ProcessTraceEvent event : processTrace.getEvents()) {
      totalTime += event.getDuration();
    }
    buf.append(
        "\n--PERFORMANCE STATS --------------------------------------------------------------------------------\n");
    if (totalTime > 0) {
      buf.append("      Duration\t      %\t Type           \tComponent Name\n");
      for (ProcessTraceEvent event : processTrace.getEvents()) {
        final int eventDuration = event.getDuration();
        if (duration > 100) {

          double pct = (double) eventDuration / totalTime;
          if (pct > 0.005d) {
            String pctStr = pctFmt.format(pct);
            buf.append(
                format("%12s", eventDuration)
                    + "ms \t"
                    + format("%7s", pctStr)
                    + "\t "
                    + cropToLength(event.getType(), 15)
                    + "\t"
                    + cropToLength(event.getComponentName(), 60)
                    + "\n");
          }
        }
      }
    }
    buf.append(
        format("%12s", totalTime)
            + "ms   TOTAL TIME ("
            + toHuman(totalTime)
            + ")\n----------------------------------------------------------------------------------------------------\n");
    System.out.println(buf);
  }
  /**
   * Constructor for the class.
   *
   * @param args command line arguments into the program - see class description
   */
  public SimpleQuestionRunCPE(String args[]) throws Exception {
    mStartTime = System.currentTimeMillis();
    if (args.length == 0) {
      args = new String[1];
      args[0] = new String("src/main/resources/CpeQuestionDescriptor.xml");
    }

    // check command line args
    if (args.length < 1) {
      printUsageMessage();
      System.exit(1);
    }

    // parse CPE descriptor
    System.out.println("Parsing CPE Descriptor");
    CpeDescription cpeDesc =
        UIMAFramework.getXMLParser().parseCpeDescription(new XMLInputSource(args[0]));
    // instantiate CPE
    System.out.println("Instantiating CPE");
    mCPE = UIMAFramework.produceCollectionProcessingEngine(cpeDesc);

    // Create and register a Status Callback Listener
    mCPE.addStatusCallbackListener(new StatusCallbackListenerImpl());

    // Start Processing
    System.out.println("Running CPE");
    mCPE.process();

    // Allow user to abort by pressing Enter
    System.out.println("To abort processing, type \"abort\" and press enter.");
    while (true) {
      String line = new BufferedReader(new InputStreamReader(System.in)).readLine();
      if ("abort".equals(line) && mCPE.isProcessing()) {
        System.out.println("Aborting...");
        mCPE.stop();
        break;
      }
    }
  }