@Nullable
 @Override
 public Future<Callbacks.ConstantAffection> request(
     String ownerClassName,
     String fieldName,
     int accessFlags,
     boolean fieldRemoved,
     boolean accessChanged) {
   final CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask.Builder task =
       CmdlineRemoteProto.Message.BuilderMessage.ConstantSearchTask.newBuilder();
   task.setOwnerClassName(ownerClassName);
   task.setFieldName(fieldName);
   task.setAccessFlags(accessFlags);
   task.setIsAccessChanged(accessChanged);
   task.setIsFieldRemoved(fieldRemoved);
   final ConstantSearchFuture future = new ConstantSearchFuture(BuildSession.this);
   final ConstantSearchFuture prev =
       mySearchTasks.put(new Pair<String, String>(ownerClassName, fieldName), future);
   if (prev != null) {
     prev.setDone();
   }
   Channels.write(
       myChannel,
       CmdlineProtoUtil.toMessage(
           mySessionId,
           CmdlineRemoteProto.Message.BuilderMessage.newBuilder()
               .setType(CmdlineRemoteProto.Message.BuilderMessage.Type.CONSTANT_SEARCH_TASK)
               .setConstantSearchTask(task.build())
               .build()));
   return future;
 }
  private void finishBuild(Throwable error, boolean hadBuildErrors, boolean markedUptodateFiles) {
    CmdlineRemoteProto.Message lastMessage = null;
    try {
      if (error != null) {
        Throwable cause = error.getCause();
        if (cause == null) {
          cause = error;
        }
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        final PrintStream stream = new PrintStream(out);
        try {
          cause.printStackTrace(stream);
        } finally {
          stream.close();
        }

        final StringBuilder messageText = new StringBuilder();
        messageText
            .append("Internal error: (")
            .append(cause.getClass().getName())
            .append(") ")
            .append(cause.getMessage());
        final String trace = out.toString();
        if (!trace.isEmpty()) {
          messageText.append("\n").append(trace);
        }
        lastMessage =
            CmdlineProtoUtil.toMessage(
                mySessionId, CmdlineProtoUtil.createFailure(messageText.toString(), cause));
      } else {
        CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Status status =
            CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Status.SUCCESS;
        if (myCanceled) {
          status = CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Status.CANCELED;
        } else if (hadBuildErrors) {
          status = CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Status.ERRORS;
        } else if (!markedUptodateFiles) {
          status = CmdlineRemoteProto.Message.BuilderMessage.BuildEvent.Status.UP_TO_DATE;
        }
        lastMessage =
            CmdlineProtoUtil.toMessage(
                mySessionId, CmdlineProtoUtil.createBuildCompletedEvent("build completed", status));
      }
    } catch (Throwable e) {
      lastMessage =
          CmdlineProtoUtil.toMessage(
              mySessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
    } finally {
      try {
        Channels.write(myChannel, lastMessage).await();
      } catch (InterruptedException e) {
        LOG.info(e);
      }
    }
  }
  private static CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings buildGlobalSettings() {
    final Map<String, String> data = new HashMap<String, String>();

    for (Map.Entry<String, String> entry : PathMacrosImpl.getGlobalSystemMacros().entrySet()) {
      data.put(entry.getKey(), FileUtil.toSystemIndependentName(entry.getValue()));
    }

    final PathMacros pathVars = PathMacros.getInstance();
    for (String name : pathVars.getAllMacroNames()) {
      final String path = pathVars.getValue(name);
      if (path != null) {
        data.put(name, FileUtil.toSystemIndependentName(path));
      }
    }

    final List<GlobalLibrary> globals = new ArrayList<GlobalLibrary>();

    fillSdks(globals);
    fillGlobalLibraries(globals);

    final CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings.Builder cmdBuilder =
        CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings.newBuilder();

    if (!data.isEmpty()) {
      for (Map.Entry<String, String> entry : data.entrySet()) {
        final String var = entry.getKey();
        final String value = entry.getValue();
        if (var != null && value != null) {
          cmdBuilder.addPathVariable(CmdlineProtoUtil.createPair(var, value));
        }
      }
    }

    if (!globals.isEmpty()) {
      for (GlobalLibrary lib : globals) {
        final CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings.GlobalLibrary.Builder
            libBuilder =
                CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings.GlobalLibrary
                    .newBuilder();
        libBuilder.setName(lib.getName()).addAllPath(lib.getPaths());
        if (lib instanceof SdkLibrary) {
          final SdkLibrary sdk = (SdkLibrary) lib;
          libBuilder.setHomePath(sdk.getHomePath());
          libBuilder.setTypeName(sdk.getTypeName());
          final String additional = sdk.getAdditionalDataXml();
          if (additional != null) {
            libBuilder.setAdditionalDataXml(additional);
          }
          final String version = sdk.getVersion();
          if (version != null) {
            libBuilder.setVersion(version);
          }
        }
        cmdBuilder.addGlobalLibrary(libBuilder.build());
      }
    }

    final String defaultCharset = EncodingManager.getInstance().getDefaultCharsetName();
    if (defaultCharset != null) {
      cmdBuilder.setGlobalEncoding(defaultCharset);
    }

    final String ignoredFilesList = FileTypeManager.getInstance().getIgnoredFilesList();
    cmdBuilder.setIgnoredFilesPatterns(ignoredFilesList);
    return cmdBuilder.build();
  }
  @Nullable
  public RequestFuture scheduleBuild(
      final Project project,
      final boolean isRebuild,
      final boolean isMake,
      final Collection<String> modules,
      final Collection<String> artifacts,
      final Collection<String> paths,
      final Map<String, String> userData,
      DefaultMessageHandler handler) {

    final String projectPath = getProjectPath(project);
    final UUID sessionId = UUID.randomUUID();
    final CmdlineRemoteProto.Message.ControllerMessage params;
    CmdlineRemoteProto.Message.ControllerMessage.GlobalSettings globals = myGlobals;
    if (globals == null) {
      globals = buildGlobalSettings();
      myGlobals = globals;
    }

    CmdlineRemoteProto.Message.ControllerMessage.FSEvent currentFSChanges = null;
    final SequentialTaskExecutor projectTaskQueue;
    synchronized (myProjectDataMap) {
      ProjectData data = myProjectDataMap.get(projectPath);
      if (data == null) {
        data = new ProjectData(new SequentialTaskExecutor(myPooledThreadExecutor));
        myProjectDataMap.put(projectPath, data);
      }
      if (isRebuild) {
        data.dropChanges();
      }
      currentFSChanges = data.getAndResetRescanFlag() ? null : data.createNextEvent();
      projectTaskQueue = data.taskQueue;
    }

    if (isRebuild) {
      params = CmdlineProtoUtil.createRebuildRequest(projectPath, userData, globals);
    } else {
      params =
          isMake
              ? CmdlineProtoUtil.createMakeRequest(
                  projectPath, modules, artifacts, userData, globals, currentFSChanges)
              : CmdlineProtoUtil.createForceCompileRequest(
                  projectPath, modules, artifacts, paths, userData, globals, currentFSChanges);
    }

    myMessageDispatcher.registerBuildMessageHandler(sessionId, handler, params);

    // ensure server is listening
    if (myListenPort < 0) {
      try {
        myListenPort = startListening();
      } catch (Exception e) {
        myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
        handler.handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), null));
        handler.sessionTerminated();
        return null;
      }
    }

    final RequestFuture<BuilderMessageHandler> future =
        new RequestFuture<BuilderMessageHandler>(
            handler,
            sessionId,
            new RequestFuture.CancelAction<BuilderMessageHandler>() {
              @Override
              public void cancel(RequestFuture<BuilderMessageHandler> future) throws Exception {
                myMessageDispatcher.cancelSession(future.getRequestID());
              }
            });

    projectTaskQueue.submit(
        new Runnable() {
          @Override
          public void run() {
            try {
              if (project.isDisposed()) {
                future.cancel(false);
                return;
              }
              myBuildsInProgress.put(projectPath, future);
              final Process process = launchBuildProcess(project, myListenPort, sessionId);
              final OSProcessHandler processHandler =
                  new OSProcessHandler(process, null) {
                    @Override
                    protected boolean shouldDestroyProcessRecursively() {
                      return true;
                    }
                  };
              final StringBuilder stdErrOutput = new StringBuilder();
              processHandler.addProcessListener(
                  new ProcessAdapter() {
                    @Override
                    public void processTerminated(ProcessEvent event) {
                      final BuilderMessageHandler handler =
                          myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
                      if (handler != null) {
                        handler.sessionTerminated();
                      }
                    }

                    @Override
                    public void onTextAvailable(ProcessEvent event, Key outputType) {
                      // re-translate builder's output to idea.log
                      final String text = event.getText();
                      if (!StringUtil.isEmpty(text)) {
                        LOG.info("BUILDER_PROCESS [" + outputType.toString() + "]: " + text.trim());
                        if (stdErrOutput.length() < 1024
                            && ProcessOutputTypes.STDERR.equals(outputType)) {
                          stdErrOutput.append(text);
                        }
                      }
                    }
                  });
              processHandler.startNotify();
              final boolean terminated = processHandler.waitFor();
              if (terminated) {
                final int exitValue = processHandler.getProcess().exitValue();
                if (exitValue != 0) {
                  final StringBuilder msg = new StringBuilder();
                  msg.append("Abnormal build process termination: ");
                  if (stdErrOutput.length() > 0) {
                    msg.append("\n").append(stdErrOutput);
                  } else {
                    msg.append("unknown error");
                  }
                  future
                      .getMessageHandler()
                      .handleFailure(
                          sessionId, CmdlineProtoUtil.createFailure(msg.toString(), null));
                }
              } else {
                future
                    .getMessageHandler()
                    .handleFailure(
                        sessionId,
                        CmdlineProtoUtil.createFailure("Disconnected from build process", null));
              }
            } catch (ExecutionException e) {
              myMessageDispatcher.unregisterBuildMessageHandler(sessionId);
              future
                  .getMessageHandler()
                  .handleFailure(sessionId, CmdlineProtoUtil.createFailure(e.getMessage(), e));
              future.getMessageHandler().sessionTerminated();
            } finally {
              myBuildsInProgress.remove(projectPath);
              future.setDone();
            }
          }
        });

    return future;
  }