Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Json RPC client-side to server-side port #3627

Merged
merged 4 commits into from
Feb 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,25 @@
*******************************************************************************/
package org.eclipse.che.wsagent.server;

import elemental.json.Json;
import elemental.json.JsonFactory;

import com.google.gson.Gson;
import com.google.gson.JsonParser;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.multibindings.MapBinder;
import com.google.inject.assistedinject.FactoryModuleBuilder;
import com.google.inject.name.Names;

import org.eclipse.che.ApiEndpointAccessibilityChecker;
import org.eclipse.che.EventBusURLProvider;
import org.eclipse.che.UriApiEndpointProvider;
import org.eclipse.che.UserTokenProvider;
import org.eclipse.che.api.auth.oauth.OAuthTokenProvider;
import org.eclipse.che.api.core.jsonrpc.RequestHandler;
import org.eclipse.che.api.core.jsonrpc.RequestTransmitter;
import org.eclipse.che.api.core.jsonrpc.impl.WebSocketToJsonRpcDispatcher;
import org.eclipse.che.api.core.jsonrpc.impl.WebSocketTransmitter;
import org.eclipse.che.api.core.jsonrpc.BuildingRequestTransmitter;
import org.eclipse.che.api.core.jsonrpc.JsonRpcFactory;
import org.eclipse.che.api.core.jsonrpc.JsonRpcMessageReceiver;
import org.eclipse.che.api.core.jsonrpc.RequestHandlerConfigurator;
import org.eclipse.che.api.core.rest.ApiInfoService;
import org.eclipse.che.api.core.rest.CoreRestModule;
import org.eclipse.che.api.core.util.FileCleaner.FileCleanerModule;
Expand All @@ -39,12 +44,14 @@
import org.eclipse.che.api.ssh.server.SshServiceClient;
import org.eclipse.che.api.user.server.spi.PreferenceDao;
import org.eclipse.che.commons.lang.Pair;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.git.impl.jgit.JGitConnectionFactory;
import org.eclipse.che.inject.DynaModule;
import org.eclipse.che.plugin.java.server.rest.WsAgentURLProvider;
import org.eclipse.che.security.oauth.RemoteOAuthTokenProvider;

import javax.inject.Named;
import javax.inject.Singleton;
import java.net.URI;

/**
Expand Down Expand Up @@ -106,14 +113,20 @@ Pair<String, String>[] propagateEventsProvider(@Named("event.bus.url") String ev

private void configureWebSocket() {
requestStaticInjection(GuiceInjectorEndpointConfigurator.class);

bind(WebSocketMessageTransmitter.class).to(BasicWebSocketMessageTransmitter.class);
bind(WebSocketMessageReceiver.class).to(WebSocketToJsonRpcDispatcher.class);

bind(WebSocketMessageReceiver.class).to(JsonRpcMessageReceiver.class);
}

private void configureJsonRpc() {
bind(RequestTransmitter.class).to(WebSocketTransmitter.class);
install(new FactoryModuleBuilder().build(JsonRpcFactory.class));
install(new FactoryModuleBuilder().build(RequestHandlerConfigurator.class));
install(new FactoryModuleBuilder().build(BuildingRequestTransmitter.class));
}

MapBinder.newMapBinder(binder(), String.class, RequestHandler.class);
@Provides
@Singleton
public JsonParser jsonParser(){
return new JsonParser();
}
}
4 changes: 4 additions & 0 deletions core/che-core-api-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-assistedinject</artifactId>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-multibindings</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.core.jsonrpc;

import org.eclipse.che.api.core.jsonrpc.transmission.EndpointIdConfigurator;

/**
* Simple factory that provides facilities to manually build JSON RPC requests
*/
public interface BuildingRequestTransmitter {
EndpointIdConfigurator newRequest();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.core.jsonrpc;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import org.eclipse.che.api.core.jsonrpc.transmission.SendConfiguratorFromOne;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Map;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
* Qualifier is used to qualify JSON RPC incoming entity. Entities can be of
* several types: {@link JsonRpcEntityType#REQUEST},
* {@link JsonRpcEntityType#RESPONSE}, {@link JsonRpcEntityType#UNDEFINED}.
* This implementations uses {@link JsonParser} to parse and analyze incoming
* entities and expects that message that is to be qualified is a valid json.
*/
@Singleton
public class JsonRpcEntityQualifier {
private static final Logger LOG = LoggerFactory.getLogger(SendConfiguratorFromOne.class);

private final JsonParser jsonParser;

@Inject
public JsonRpcEntityQualifier(JsonParser jsonParser) {
this.jsonParser = jsonParser;
}

public JsonRpcEntityType qualify(String message) {
checkNotNull(message, "Message must not be null");
checkArgument(!message.isEmpty(), "Message must not be empty");
LOG.debug("Qualifying message: " + message);

JsonObject jsonObject = jsonParser.parse(message).getAsJsonObject();
LOG.debug("Json keys: " + jsonObject.entrySet().stream().map(Map.Entry::getKey).collect(Collectors.toSet()));

if (jsonObject.has("method")) {
LOG.debug("Qualified to request");

return JsonRpcEntityType.REQUEST;
} else if (jsonObject.has("error") != jsonObject.has("result")) {
LOG.debug("Qualified to response");

return JsonRpcEntityType.RESPONSE;
} else {
LOG.debug("Qualified to undefined");

return JsonRpcEntityType.UNDEFINED;
}
}

public enum JsonRpcEntityType {
REQUEST,
RESPONSE,
UNDEFINED
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.core.jsonrpc;

import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;

import org.eclipse.che.api.core.jsonrpc.transmission.SendConfiguratorFromOne;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Singleton;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
* Simple class to validate if we're dealing with a properly constructed
* json represented by a string. We use {@link JsonParser} to parse string
* message and to rise exception if json is incorrect.
*/
@Singleton
public class JsonRpcEntityValidator {
private static final Logger LOG = LoggerFactory.getLogger(SendConfiguratorFromOne.class);

private final JsonParser jsonParser;

@Inject
public JsonRpcEntityValidator(JsonParser jsonParser) {
this.jsonParser = jsonParser;
}

public void validate(String message) throws JsonRpcException {
checkNotNull(message, "Message must not be null");
checkArgument(!message.isEmpty(), "Message must not be empty");

LOG.debug("Validating message: {}", message);

try {
jsonParser.parse(message);

LOG.debug("Validation successful");
} catch (JsonParseException e) {
LOG.debug("Validation failed: {}", e.getMessage(), e);

throw new JsonRpcException(-32700, "An error occurred on the server while parsing the JSON text:", e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.core.jsonrpc;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
* Represents JSON RPC error object. Can be constructed out of
* stringified json object or by passing specific parameters.
* Use {@link JsonRpcFactory#createError(int, String)} or
* {@link JsonRpcFactory#createError(String)} to get an instance.
*/
public class JsonRpcError {
private final int code;
private final String message;

@AssistedInject
public JsonRpcError(@Assisted("code") int code, @Assisted("message") String message) {
this.code = code;
this.message = message;
}

@AssistedInject
public JsonRpcError(@Assisted("message") String message, JsonParser jsonParser) {
checkNotNull(message, "Message must not be null");
checkArgument(!message.isEmpty(), "Message must not be empty");

this.code = getCode(message, jsonParser);
this.message = getMessage(message, jsonParser);
}

private static String getMessage(String message, JsonParser jsonParser) {
return jsonParser.parse(message).getAsJsonObject().get("message").getAsString();
}

private static int getCode(String message, JsonParser jsonParser) {
return jsonParser.parse(message).getAsJsonObject().get("code").getAsInt();
}

public JsonObject toJsonObject() {
JsonObject error = new JsonObject();
error.addProperty("code", code);
error.addProperty("message", message);
return error;
}

public int getCode() {
return code;
}

public String getMessage() {
return message;
}

@Override
public String toString() {
return toJsonObject().toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.api.core.jsonrpc;

import org.eclipse.che.api.core.websocket.WebSocketMessageTransmitter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Singleton;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

/**
* Transmits an instance of {@link JsonRpcException} to specific endpoint
*/
@Singleton
public class JsonRpcErrorTransmitter {
private static final Logger LOG = LoggerFactory.getLogger(JsonRpcErrorTransmitter.class);

private final WebSocketMessageTransmitter transmitter;
private final JsonRpcFactory jsonRpcFactory;

@Inject
public JsonRpcErrorTransmitter(WebSocketMessageTransmitter transmitter, JsonRpcFactory jsonRpcFactory) {
this.transmitter = transmitter;
this.jsonRpcFactory = jsonRpcFactory;
}

public void transmit(String endpointId, JsonRpcException e) {
checkNotNull(endpointId, "Endpoint ID must not be null");
checkArgument(!endpointId.isEmpty(), "Endpoint ID must not be empty");

LOG.debug("Transmitting a JSON RPC error out of: " + e.getMessage());

JsonRpcError error = jsonRpcFactory.createError(e.getCode(), e.getMessage());
JsonRpcResponse response = jsonRpcFactory.createResponse(e.getId(), null, error);
transmitter.transmit(endpointId, response.toString());
}
}
Loading