org.hertsstack.http.InternalHttpServlet Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of herts-http Show documentation
Show all versions of herts-http Show documentation
Herts real time framework for Http
package org.hertsstack.http;
import org.hertsstack.core.context.HertsType;
import org.hertsstack.core.exception.HttpServerBuildException;
import org.hertsstack.core.modelx.InternalHttpErrorResponse;
import org.hertsstack.core.exception.InvalidMessageException;
import org.hertsstack.core.exception.http.HttpErrorException;
import org.hertsstack.core.logger.Logging;
import org.hertsstack.core.modelx.RegisteredMethod;
import org.hertsstack.core.service.HertsService;
import org.hertsstack.serializer.MessageSerializeType;
import org.hertsstack.serializer.MessageSerializer;
import org.hertsstack.serializer.MessageJsonParsingException;
import org.hertsstack.metrics.HertsMetrics;
import org.hertsstack.metrics.HertsMetricsServer;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Herts http server core
*
* @author Herts Contributer
*/
public class InternalHttpServlet extends HttpServlet {
private static final java.util.logging.Logger logger = Logging.getLogger(InternalHttpServlet.class.getSimpleName());
private static final MessageSerializer hertsSerializer = new MessageSerializer(MessageSerializeType.Json);
// HertsMetrics instance
private final HertsMetrics hertsHttpMetrics;
// Key is endpoint
private final ConcurrentMap methodMap;
// Key is HertsService string name
private final ConcurrentMap callerMap;
// Base endpoint list
private final List baseEndpoints;
/**
* Gateway Server.
* Create InternalHttpServlet instance by HertsService interface Class list.
*
* @param hertsRpcServices Class of HertsService interface
* @param hertsHttpMetrics HertsMetrics
* @param caller HertsHttpCaller
* @return HertsHttpServerCore
*/
public static InternalHttpServlet createGateway(List> hertsRpcServices,
HertsMetrics hertsHttpMetrics,
InternalHttpCaller caller) {
List baseEndpoints = new ArrayList<>();
ConcurrentMap callerMap = new ConcurrentHashMap<>();
ConcurrentMap methodMap = new ConcurrentHashMap<>();
for (Class> hertsServiceClass : hertsRpcServices) {
if (!hertsServiceClass.isInterface()) {
throw new HttpServerBuildException("Herts service is not interface");
}
String serviceName = hertsServiceClass.getSimpleName();
String baseEndpoint = "/gateway/" + serviceName;
baseEndpoints.add(baseEndpoint);
try {
Method[] defMethods = hertsServiceClass.getDeclaredMethods();
for (Method method : defMethods) {
String endpoint = baseEndpoint + "/" + method.getName();
methodMap.put(endpoint, method);
}
callerMap.put(serviceName, caller);
} catch (Exception ex) {
throw new HttpServerBuildException("Failed to build http server", ex);
}
}
return new InternalHttpServlet(hertsHttpMetrics, baseEndpoints, methodMap, callerMap);
}
/**
* API Server.
* Create InternalHttpServlet instance by HertsService list.
*
* @param hertsRpcServices HertsService list
* @param hertsHttpMetrics HertsMetrics
* @param metricsServer HertsMetricsServer
* @param caller HertsHttpCaller
* @return HertsHttpServerCore
*/
public static InternalHttpServlet createByHertsService(List hertsRpcServices,
HertsMetrics hertsHttpMetrics,
HertsMetricsServer metricsServer,
@Nullable InternalHttpCaller caller) {
List baseEndpoints = new ArrayList<>();
ConcurrentMap callerMap = new ConcurrentHashMap<>();
ConcurrentMap methodMap = new ConcurrentHashMap<>();
for (HertsService hertsService : hertsRpcServices) {
String serviceName = hertsService.getClass().getInterfaces()[0].getSimpleName();
String baseEndpoint = "/api/" + serviceName;
baseEndpoints.add(baseEndpoint);
try {
Class> thisClass = Class.forName(hertsService.getClass().getName());
Method[] defMethods = thisClass.getDeclaredMethods();
ConcurrentMap registeredMethods = new ConcurrentHashMap<>();
for (Method method : defMethods) {
String endpoint = baseEndpoint + "/" + method.getName();
methodMap.put(endpoint, method);
Parameter[] parameters = thisClass.getMethod(method.getName(), method.getParameterTypes()).getParameters();
RegisteredMethod registeredMethod = new RegisteredMethod(
HertsType.Http,
hertsService.getClass().getSimpleName(),
hertsService.getClass().getSimpleName(),
method.getName(),
method.getReturnType(),
method.getParameterTypes()
);
registeredMethod.setParameters(parameters);
registeredMethods.put(method.getName(), registeredMethod);
}
if (caller != null) {
callerMap.put(serviceName, caller);
} else if (hertsHttpMetrics.isMetricsEnabled()) {
callerMap.put(serviceName, new HertsHttpMetricsCaller(hertsService, hertsHttpMetrics,
hertsSerializer, metricsServer, registeredMethods, serviceName));
} else {
callerMap.put(serviceName, new HertsHttpSimpleCaller(
hertsService, hertsSerializer, registeredMethods));
}
} catch (Exception ex) {
throw new HttpServerBuildException("Failed to build http server", ex);
}
}
return new InternalHttpServlet(hertsHttpMetrics, baseEndpoints, methodMap, callerMap);
}
/**
* Private constructor.
* Please see factory method.
*
* @param hertsHttpMetrics HertsMetrics
* @param baseEndpoints Server base endpoint list
* @param methodMap Method info
* @param callerMap Caller info
*/
private InternalHttpServlet(HertsMetrics hertsHttpMetrics, List baseEndpoints,
ConcurrentMap methodMap, ConcurrentMap callerMap) {
this.hertsHttpMetrics = hertsHttpMetrics;
this.hertsHttpMetrics.register();
this.baseEndpoints = baseEndpoints;
this.methodMap = methodMap;
this.callerMap = callerMap;
}
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String uri = request.getRequestURI();
if (!isMetricsEndpoint(uri)) {
InternalHttpErrorResponse err = genErrorResponse(HttpErrorException.StatusCode.Status404, "Not found");
setError(HttpServletResponse.SC_NOT_FOUND, response, err);
return;
}
this.callerMap.entrySet().iterator().next().getValue().setMetricsResponse(response);
}
@Override
public void doOptions(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setStatus(HttpServletResponse.SC_OK);
if (isMetricsEndpoint(request.getRequestURI())) {
response.setHeader("Allow", "GET, HEAD, OPTIONS");
return;
}
ParedHttpRequestInfo requestInfo = parsedRequestInfo(request.getRequestURI());
if (requestInfo != null){
response.setHeader("Allow", "POST, HEAD, OPTIONS");
return;
}
InternalHttpErrorResponse err = genErrorResponse(HttpErrorException.StatusCode.Status404, "Not found");
setError(HttpServletResponse.SC_NOT_FOUND, response, err);
}
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
String uri = request.getRequestURI();
ParedHttpRequestInfo requestInfo = parsedRequestInfo(uri);
if (requestInfo == null) {
InternalHttpErrorResponse err = genErrorResponse(HttpErrorException.StatusCode.Status404, "Not found");
setError(HttpServletResponse.SC_NOT_FOUND, response, err);
return;
}
try {
this.callerMap.get(requestInfo.getServiceName()).post(requestInfo.getServiceName(), requestInfo.getHertsMethod(), request, response);
} catch (InvalidMessageException | MessageJsonParsingException ex) {
InternalHttpErrorResponse err = genErrorResponse(HttpErrorException.StatusCode.Status400, ex.getMessage());
setError(HttpServletResponse.SC_BAD_REQUEST, response, err);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof HttpErrorException) {
HttpErrorException exception = (HttpErrorException) cause;
InternalHttpErrorResponse err = genErrorResponse(exception.getStatusCode(), ex.getMessage());
setError(exception.getStatusCode().getIntegerCode(), response, err);
} else {
ex.printStackTrace();
InternalHttpErrorResponse err = genErrorResponse(HttpErrorException.StatusCode.Status500, ex.getMessage());
setError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response, err);
}
} catch (Exception ex) {
ex.printStackTrace();
InternalHttpErrorResponse err = genErrorResponse(HttpErrorException.StatusCode.Status500, ex.getMessage());
setError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, response, err);
}
}
public String[] getEndpoints() {
return this.methodMap.keySet().toArray(new String[0]);
}
private ParedHttpRequestInfo parsedRequestInfo(String uri) {
String serviceName = extractServiceName(uri);
if (serviceName == null) {
return null;
}
Method hertsMethod = this.methodMap.get(uri);
if (hertsMethod == null) {
return null;
}
return new ParedHttpRequestInfo(serviceName, hertsMethod);
}
private String extractServiceName(String url) {
if (url == null || url.isEmpty()) {
return null;
}
String[] splitUrls = url.split("/");
if (splitUrls.length < 3) {
return null;
}
return splitUrls[2];
}
private boolean isMetricsEndpoint(String url) {
return this.hertsHttpMetrics.isMetricsEnabled() && url.equals("/metricsz");
}
public static InternalHttpErrorResponse genErrorResponse(HttpErrorException.StatusCode statusCodeEnum, String message) {
InternalHttpErrorResponse hertsResponse = new InternalHttpErrorResponse();
hertsResponse.setStatusCodeEnum(statusCodeEnum);
hertsResponse.setMessage(message);
return hertsResponse;
}
private void setError(int statusCode, HttpServletResponse response, InternalHttpErrorResponse errorResponse) throws IOException {
response.setStatus(statusCode);
HertsHttpCallerBase.setWriter(response.getWriter(), hertsSerializer.serializeAsStr(errorResponse));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy