public static ParTask<String> search(String[] queries) {
   List<Task<String>> queryTasks = new ArrayList<>();
   for (String query : queries) {
     queryTasks.add(firstImageUrl(query));
   }
   ParTask<String> par = Tasks.par(queryTasks);
   ENGINE.run(par);
   return par;
 }
  @Override
  protected void doRunExample(final Engine engine) throws Exception {
    final int[] toSort = createRandomArray(10, new Random());

    final Task<int[]> mergeSort = mergeSort(toSort, new Range(0, toSort.length));
    engine.run(mergeSort);
    mergeSort.await();

    System.out.println("Before sort: " + Arrays.toString(toSort));
    System.out.println("After  sort: " + Arrays.toString(mergeSort.get()));
    Arrays.sort(toSort);
    System.out.println("Java   sort: " + Arrays.toString(toSort));

    ExampleUtil.printTracingResults(mergeSort);
  }
  public static void main(String[] args) throws InterruptedException {
    // Test sequential model: Task.par().andThen()
    Task<String> cageImg = firstImageUrl("nicholas cage");
    Task<String> yazhouImg = firstImageUrl("yazhou cao");
    Task<String> asiaImg = firstImageUrl("asia cao");

    final String host = "www.google.com";

    Tuple3Task<String, String, String> fanInTask =
        Task.par(cageImg, yazhouImg, asiaImg)
            .andThen((a, b, c) -> System.out.println(host + a + "\n" + host + b + "\n" + host + c));
    ENGINE.run(fanInTask);

    // Blocking until promise resolved
    fanInTask.await();

    // Test search()
    String[] queries = {"tom hardy", "steve jobs", "super mario"};
    ParTask<String> imageList = search(queries);
    imageList.await();
    System.out.println("Search results:\n" + imageList.getSuccessful());
  }
  @SuppressWarnings("deprecation")
  private void doInvoke(
      final ResourceMethodDescriptor descriptor,
      final RequestExecutionCallback<Object> callback,
      final RequestExecutionReportBuilder requestExecutionReportBuilder,
      final Object resource,
      final Object... arguments)
      throws IllegalAccessException {
    Method method = descriptor.getMethod();

    try {
      switch (descriptor.getInterfaceType()) {
        case CALLBACK:
          int callbackIndex = descriptor.indexOfParameterType(ParamType.CALLBACK);
          final RequestExecutionReport executionReport =
              getRequestExecutionReport(requestExecutionReportBuilder);

          // Delegate the callback call to the request execution callback along with the
          // request execution report.
          arguments[callbackIndex] =
              new Callback<Object>() {
                @Override
                public void onError(Throwable e) {
                  callback.onError(
                      e instanceof RestLiServiceException
                          ? e
                          : new RestLiServiceException(HttpStatus.S_500_INTERNAL_SERVER_ERROR, e),
                      executionReport);
                }

                @Override
                public void onSuccess(Object result) {
                  callback.onSuccess(result, executionReport);
                }
              };

          method.invoke(resource, arguments);
          // App code should use the callback
          break;

        case SYNC:
          Object applicationResult = method.invoke(resource, arguments);
          callback.onSuccess(
              applicationResult, getRequestExecutionReport(requestExecutionReportBuilder));
          break;

        case PROMISE:
          if (!checkEngine(callback, descriptor, requestExecutionReportBuilder)) {
            break;
          }
          int contextIndex = descriptor.indexOfParameterType(ParamType.PARSEQ_CONTEXT_PARAM);

          if (contextIndex == -1) {
            contextIndex = descriptor.indexOfParameterType(ParamType.PARSEQ_CONTEXT);
          }
          // run through the engine to get the context
          Task<Object> restliTask = new RestLiParSeqTask(arguments, contextIndex, method, resource);

          // propagate the result to the callback
          restliTask.addListener(
              new CallbackPromiseAdapter<Object>(
                  callback, restliTask, requestExecutionReportBuilder));
          _engine.run(restliTask);
          break;

        case TASK:
          if (!checkEngine(callback, descriptor, requestExecutionReportBuilder)) {
            break;
          }

          // addListener requires Task<Object> in this case
          @SuppressWarnings("unchecked")
          Task<Object> task = (Task<Object>) method.invoke(resource, arguments);
          if (task == null) {
            callback.onError(
                new RestLiServiceException(
                    HttpStatus.S_500_INTERNAL_SERVER_ERROR, "Error in application code: null Task"),
                getRequestExecutionReport(requestExecutionReportBuilder));
          } else {
            task.addListener(
                new CallbackPromiseAdapter<Object>(callback, task, requestExecutionReportBuilder));
            _engine.run(task);
          }
          break;

        default:
          throw new AssertionError("Unexpected interface type " + descriptor.getInterfaceType());
      }
    } catch (InvocationTargetException e) {
      // Method runtime exceptions ar expected to fail with a top level
      // InvocationTargetException wrapped around the root cause.
      if (RestLiServiceException.class.isAssignableFrom(e.getCause().getClass())) {
        RestLiServiceException restLiServiceException = (RestLiServiceException) e.getCause();
        callback.onError(
            restLiServiceException, getRequestExecutionReport(requestExecutionReportBuilder));
      } else {
        callback.onError(
            new RestLiServiceException(
                HttpStatus.S_500_INTERNAL_SERVER_ERROR,
                _errorResponseBuilder.getInternalErrorMessage(),
                e.getCause()),
            getRequestExecutionReport(requestExecutionReportBuilder));
      }
    }
  }