// Copyright 2017 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.intellij.ijaas; import com.google.common.base.Throwables; import com.google.devtools.intellij.ijaas.handlers.EchoHandler; import com.google.devtools.intellij.ijaas.handlers.JavaCompleteHandler; import com.google.devtools.intellij.ijaas.handlers.JavaGetImportCandidatesHandler; import com.google.devtools.intellij.ijaas.handlers.JavaSrcUpdateHandler; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonIOException; import com.google.gson.JsonStreamParser; import com.google.gson.stream.JsonWriter; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.annotation.Nullable; public class IjaasServer { private final int port; private final Gson gson = new Gson(); private final HashMap<String, IjaasHandler> handlers = new HashMap<>(); IjaasServer(int port) { this.port = port; // TODO: Add handlers handlers.put("echo", new EchoHandler()); handlers.put("java_complete", new JavaCompleteHandler()); handlers.put("java_src_update", new JavaSrcUpdateHandler()); handlers.put("java_get_import_candidates", new JavaGetImportCandidatesHandler()); } void start() { new Thread( () -> { ExecutorService executorService = Executors.newCachedThreadPool(); try (ServerSocket serverSocket = new ServerSocket(port, 0, InetAddress.getLoopbackAddress())) { while (true) { Socket socket = serverSocket.accept(); executorService.execute(() -> process(socket)); } } catch (IOException e) { throw new RuntimeException(e); } }) .start(); } private void process(Socket socket) { try { try { JsonStreamParser parser = new JsonStreamParser( new InputStreamReader( new BufferedInputStream(socket.getInputStream()), StandardCharsets.UTF_8)); try (JsonWriter writer = gson.newJsonWriter( new OutputStreamWriter( new BufferedOutputStream(socket.getOutputStream()), StandardCharsets.UTF_8))) { // There are several top-level values. writer.setLenient(true); while (parser.hasNext()) { JsonArray request = parser.next().getAsJsonArray(); long id = request.get(0).getAsLong(); GenericRequest genericRequest = gson.fromJson(request.get(1), GenericRequest.class); JsonElement response; try { response = gson.toJsonTree(new GenericResponse(processRequest(genericRequest))); } catch (Exception e) { response = gson.toJsonTree( new ErrorResponse(e.getMessage(), Throwables.getStackTraceAsString(e))); } writer.beginArray(); writer.value(id); gson.toJson(response, writer); writer.endArray(); writer.flush(); } } } finally { socket.close(); } } catch (JsonIOException e) { Throwable t = e.getCause(); if (t instanceof EOFException) { // Ignore. This happens when the input is empty. } else { throw new RuntimeException(e); } } catch (IOException e) { throw new RuntimeException(e); } } private JsonElement processRequest(GenericRequest genericRequest) { if (genericRequest == null) { throw new RuntimeException("method is required"); } IjaasHandler handler = handlers.get(genericRequest.method); if (handler == null) { throw new RuntimeException(genericRequest.method + "is not found"); } return handler.handle(genericRequest.params); } private static class GenericRequest { @Nullable String method; @Nullable JsonElement params; } private static class GenericResponse { private final JsonElement result; GenericResponse(JsonElement result) { this.result = result; } } private static class ErrorResponse { private final String error; private final String cause; ErrorResponse(String error, String cause) { this.error = error; this.cause = cause; } } }