/*******************************************************************************
* 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.commons;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.core.websocket.commons.WebSocketMessageTransmitter;
import org.slf4j.Logger;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Manages request handlers. There are nine types of such handlers that differs
* by the type and number of incoming parameters and outgoing results:
* <ul>
* <li>{@link NoneToNoneHandler} - to receive a notification w/o parameters</li>
* <li>{@link NoneToOneHandler} - to receive a request w/o parameters and a single result</li>
* <li>{@link NoneToManyHandler} - to receive a request w/o parameters and multiple results</li>
* <li>{@link OneToNoneHandler} - to receive a notification with a single parameter</li>
* <li>{@link OneToOneHandler} - to receive a request with a single parameter and a single result</li>
* <li>{@link OneToManyHandler}- to receive a request with a single parameter and multiple results </li>
* <li>{@link ManyToNoneHandler} - to receive a notification with multiple parameters</li>
* <li>{@link ManyToOneHandler} - to receive request with multiple parameters and a single result</li>
* <li>{@link ManyToManyHandler} - to receive request with multiple parameters and multiple results</li>
* </ul>
*/
@Singleton
public class RequestHandlerManager {
private final static Logger LOGGER = getLogger(RequestHandlerManager.class);
private final Map<String, Category> methodToCategory = new ConcurrentHashMap<>();
private final Map<String, OneToOneHandler> oneToOneHandlers = new ConcurrentHashMap<>();
private final Map<String, OneToManyHandler> oneToManyHandlers = new ConcurrentHashMap<>();
private final Map<String, OneToNoneHandler> oneToNoneHandlers = new ConcurrentHashMap<>();
private final Map<String, ManyToOneHandler> manyToOneHandlers = new ConcurrentHashMap<>();
private final Map<String, ManyToManyHandler> manyToManyHandlers = new ConcurrentHashMap<>();
private final Map<String, ManyToNoneHandler> manyToNoneHandlers = new ConcurrentHashMap<>();
private final Map<String, NoneToOneHandler> noneToOneHandlers = new ConcurrentHashMap<>();
private final Map<String, NoneToManyHandler> noneToManyHandlers = new ConcurrentHashMap<>();
private final Map<String, NoneToNoneHandler> noneToNoneHandlers = new ConcurrentHashMap<>();
private final WebSocketMessageTransmitter transmitter;
private final JsonRpcComposer dtoComposer;
private final JsonRpcMarshaller marshaller;
@Inject
public RequestHandlerManager(WebSocketMessageTransmitter transmitter, JsonRpcComposer dtoComposer, JsonRpcMarshaller marshaller) {
this.transmitter = transmitter;
this.dtoComposer = dtoComposer;
this.marshaller = marshaller;
}
public synchronized <P, R> void registerOneToOne(String method, Class<P> pClass, Class<R> rClass, BiFunction<String, P, R> biFunction) {
mustNotBeRegistered(method);
methodToCategory.put(method, Category.ONE_TO_ONE);
oneToOneHandlers.put(method, new OneToOneHandler<>(pClass, rClass, biFunction));
}
public synchronized <P, R> void registerOneToMany(String method, Class<P> pClass, Class<R> rClass, BiFunction<String, P, List<R>> biFunction) {
mustNotBeRegistered(method);
methodToCategory.put(method, Category.ONE_TO_MANY);
oneToManyHandlers.put(method, new OneToManyHandler<>(pClass, rClass, biFunction));
}
public synchronized <P> void registerOneToNone(String method, Class<P> pClass, BiConsumer<String, P> biConsumer) {
mustNotBeRegistered(method);
methodToCategory.put(method, Category.ONE_TO_NONE);
oneToNoneHandlers.put(method, new OneToNoneHandler<>(pClass, biConsumer));
}
public synchronized <P, R> void registerManyToOne(String method, Class<P> pClass, Class<R> rClass, BiFunction<String, List<P>, R> biFunction) {
mustNotBeRegistered(method);
methodToCategory.put(method, Category.MANY_TO_ONE);
manyToOneHandlers.put(method, new ManyToOneHandler<>(pClass, rClass, biFunction));
}
public synchronized <P, R> void registerManyToMany(String method, Class<P> pClass, Class<R> rClass, BiFunction<String, List<P>, List<R>> function) {
mustNotBeRegistered(method);
methodToCategory.put(method, Category.MANY_TO_MANY);
manyToManyHandlers.put(method, new ManyToManyHandler<>(pClass, rClass, function));
}
public synchronized <P> void registerManyToNone(String method, Class<P> pClass, BiConsumer<String, List<P>> biConsumer) {
mustNotBeRegistered(method);
methodToCategory.put(method, Category.MANY_TO_NONE);
manyToNoneHandlers.put(method, new ManyToNoneHandler<>(pClass, biConsumer));
}
public synchronized <R> void registerNoneToOne(String method, Class<R> rClass, Function<String, R> function) {
mustNotBeRegistered(method);
methodToCategory.put(method, Category.NONE_TO_ONE);
noneToOneHandlers.put(method, new NoneToOneHandler<>(rClass, function));
}
public synchronized <R> void registerNoneToMany(String method, Class<R> rClass, Function<String, List<R>> function) {
mustNotBeRegistered(method);
methodToCategory.put(method, Category.NONE_TO_MANY);
noneToManyHandlers.put(method, new NoneToManyHandler<>(rClass, function));
}
public synchronized void registerNoneToNone(String method, Consumer<String> consumer) {
mustNotBeRegistered(method);
methodToCategory.put(method, Category.NONE_TO_NONE);
noneToNoneHandlers.put(method, new NoneToNoneHandler(consumer));
}
public boolean isRegistered(String method) {
return methodToCategory.containsKey(method);
}
public void handle(String endpointId, String requestId, String method, JsonRpcParams params) {
mustBeRegistered(method);
switch (methodToCategory.get(method)) {
case ONE_TO_ONE:
OneToOneHandler oneToOneHandler = oneToOneHandlers.get(method);
transmitOne(endpointId, requestId, oneToOneHandler.handle(endpointId, params));
break;
case ONE_TO_MANY:
OneToManyHandler oneToManyHandler = oneToManyHandlers.get(method);
transmitMany(endpointId, requestId, oneToManyHandler.handle(endpointId, params));
break;
case MANY_TO_ONE:
ManyToOneHandler manyToOneHandler = manyToOneHandlers.get(method);
transmitOne(endpointId, requestId, manyToOneHandler.handle(endpointId, params));
break;
case MANY_TO_MANY:
ManyToManyHandler manyToManyHandler = manyToManyHandlers.get(method);
transmitMany(endpointId, requestId, manyToManyHandler.handle(endpointId, params));
break;
case NONE_TO_ONE:
NoneToOneHandler noneToOneHandler = noneToOneHandlers.get(method);
transmitOne(endpointId, requestId, noneToOneHandler.handle(endpointId));
break;
case NONE_TO_MANY:
NoneToManyHandler noneToManyHandler = noneToManyHandlers.get(method);
transmitMany(endpointId, requestId, noneToManyHandler.handle(endpointId));
break;
default:
LOGGER.error("Something went wrong trying to find out handler category");
}
}
public void handle(String endpointId, String method, JsonRpcParams params) {
mustBeRegistered(method);
switch (methodToCategory.get(method)) {
case ONE_TO_NONE:
oneToNoneHandlers.get(method).handle(endpointId, params);
break;
case MANY_TO_NONE:
manyToNoneHandlers.get(method).handle(endpointId, params);
break;
case NONE_TO_NONE:
noneToNoneHandlers.get(method).handle(endpointId);
break;
default:
LOGGER.error("Something went wrong trying to find out handler category");
}
}
private void mustBeRegistered(String method) {
if (!isRegistered(method)) {
String message = "Method '" + method + "' is not registered";
LOGGER.error(message);
throw new IllegalStateException(message);
}
}
private void mustNotBeRegistered(String method) {
if (isRegistered(method)) {
String message = "Method '" + method + "' is already registered";
LOGGER.error(message);
throw new IllegalStateException(message);
}
}
private void transmitOne(String endpointId, String id, Object result) {
JsonRpcResult jsonRpcResult = new JsonRpcResult(result);
JsonRpcResponse jsonRpcResponse = new JsonRpcResponse(id, jsonRpcResult, null);
String message = marshaller.marshall(jsonRpcResponse);
transmitter.transmit(endpointId, message);
}
private void transmitMany(String endpointId, String id, List<?> result) {
JsonRpcResult jsonRpcResult = new JsonRpcResult(result);
JsonRpcResponse jsonRpcResponse = new JsonRpcResponse(id, jsonRpcResult, null);
String message = marshaller.marshall(jsonRpcResponse);
transmitter.transmit(endpointId, message);
}
public enum Category {
ONE_TO_ONE,
ONE_TO_MANY,
ONE_TO_NONE,
MANY_TO_ONE,
MANY_TO_MANY,
MANY_TO_NONE,
NONE_TO_ONE,
NONE_TO_MANY,
NONE_TO_NONE
}
private class OneToOneHandler<P, R> {
final private Class<P> pClass;
final private Class<R> rClass;
final private BiFunction<String, P, R> biFunction;
private OneToOneHandler(Class<P> pClass, Class<R> rClass, BiFunction<String, P, R> biFunction) {
this.pClass = pClass;
this.rClass = rClass;
this.biFunction = biFunction;
}
private R handle(String endpointId, JsonRpcParams params) {
P dto = dtoComposer.composeOne(params, pClass);
return biFunction.apply(endpointId, dto);
}
}
private class OneToManyHandler<P, R> {
final private Class<P> pClass;
final private Class<R> rClass;
final private BiFunction<String, P, List<R>> biFunction;
private OneToManyHandler(Class<P> pClass, Class<R> rClass, BiFunction<String, P, List<R>> biFunction) {
this.pClass = pClass;
this.rClass = rClass;
this.biFunction = biFunction;
}
private List<R> handle(String endpointId, JsonRpcParams params) {
P dto = dtoComposer.composeOne(params, pClass);
return biFunction.apply(endpointId, dto);
}
}
private class OneToNoneHandler<P> {
final private Class<P> pClass;
final private BiConsumer<String, P> biConsumer;
private OneToNoneHandler(Class<P> pClass, BiConsumer<String, P> biConsumer) {
this.pClass = pClass;
this.biConsumer = biConsumer;
}
private void handle(String endpointId, JsonRpcParams params) {
P dto = dtoComposer.composeOne(params, pClass);
biConsumer.accept(endpointId, dto);
}
}
private class ManyToOneHandler<P, R> {
final private Class<P> pClass;
final private Class<R> rClass;
final private BiFunction<String, List<P>, R> biFunction;
private ManyToOneHandler(Class<P> pClass, Class<R> rClass, BiFunction<String, List<P>, R> biFunction) {
this.pClass = pClass;
this.rClass = rClass;
this.biFunction = biFunction;
}
private R handle(String endpointId, JsonRpcParams params) {
List<P> dto = dtoComposer.composeMany(params, pClass);
return biFunction.apply(endpointId, dto);
}
}
private class ManyToManyHandler<P, R> {
final private Class<P> pClass;
final private Class<R> rClass;
final private BiFunction<String, List<P>, List<R>> biFunction;
private ManyToManyHandler(Class<P> pClass, Class<R> rClass,
BiFunction<String, List<P>, List<R>> biFunction) {
this.pClass = pClass;
this.rClass = rClass;
this.biFunction = biFunction;
}
private List<R> handle(String endpointId, JsonRpcParams params) {
List<P> dto = dtoComposer.composeMany(params, pClass);
return biFunction.apply(endpointId, dto);
}
}
private class ManyToNoneHandler<P> {
final private Class<P> pClass;
final private BiConsumer<String, List<P>> biConsumer;
private ManyToNoneHandler(Class<P> pClass, BiConsumer<String, List<P>> biConsumer) {
this.pClass = pClass;
this.biConsumer = biConsumer;
}
private void handle(String endpointId, JsonRpcParams params) {
List<P> dto = dtoComposer.composeMany(params, pClass);
biConsumer.accept(endpointId, dto);
}
}
private class NoneToOneHandler<R> {
final private Class<R> rClass;
final private Function<String, R> function;
private NoneToOneHandler(Class<R> rClass, Function<String, R> function) {
this.rClass = rClass;
this.function = function;
}
private R handle(String endpointId) {
return function.apply(endpointId);
}
}
private class NoneToManyHandler<R> {
final private Class<R> rClass;
final private Function<String, List<R>> function;
private NoneToManyHandler(Class<R> rClass, Function<String, List<R>> function) {
this.rClass = rClass;
this.function = function;
}
private List<R> handle(String endpointId) {
return function.apply(endpointId);
}
}
private class NoneToNoneHandler {
final private Consumer<String> consumer;
private NoneToNoneHandler(Consumer<String> consumer) {
this.consumer = consumer;
}
private void handle(String endpointId) {
consumer.accept(endpointId);
}
}
}