All Downloads are FREE. Search and download functionalities are using the official Maven repository.

info.unterrainer.commons.httpserver.HttpServer Maven / Gradle / Ivy

There is a newer version: 0.3.14
Show newest version
package info.unterrainer.commons.httpserver;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;

import info.unterrainer.commons.httpserver.accessmanager.HttpAccessManager;
import info.unterrainer.commons.httpserver.accessmanager.UserAccessInterceptor;
import info.unterrainer.commons.httpserver.daos.CoreDaoProvider;
import info.unterrainer.commons.httpserver.enums.Attribute;
import info.unterrainer.commons.httpserver.enums.ResponseType;
import info.unterrainer.commons.httpserver.exceptions.HttpException;
import info.unterrainer.commons.httpserver.exceptions.NotFoundException;
import info.unterrainer.commons.httpserver.exceptions.UnauthorizedException;
import info.unterrainer.commons.httpserver.handlers.AppNameHandler;
import info.unterrainer.commons.httpserver.handlers.AppVersionHandler;
import info.unterrainer.commons.httpserver.handlers.DateTimeHandler;
import info.unterrainer.commons.httpserver.handlers.HealthHandler;
import info.unterrainer.commons.httpserver.handlers.PostmanCollectionHandler;
import info.unterrainer.commons.httpserver.jsons.MessageJson;
import info.unterrainer.commons.jreutils.ShutdownHook;
import info.unterrainer.commons.rdbutils.entities.BasicJpa;
import info.unterrainer.commons.serialization.JsonMapper;
import info.unterrainer.commons.serialization.jsons.BasicJson;
import io.javalin.Javalin;
import io.javalin.core.security.Role;
import io.javalin.http.Context;
import io.javalin.http.Handler;
import io.javalin.http.HandlerType;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import okio.BufferedSource;
import okio.GzipSource;
import okio.Okio;

@Slf4j
public class HttpServer {

	private static final String VERSION_FQN = "info.unterrainer.commons.httpserver.Information";
	private Javalin javalin;
	private HttpServerConfiguration config;
	private String applicationName;
	private JsonMapper jsonMapper;
	private MapperFactory orikaFactory;
	private final List handlerGroups = new ArrayList<>();
	private List handlerInstances = new ArrayList<>();
	ExecutorService executorService;
	List appVersionFqns;
	@Getter
	@Setter
	private UserAccessInterceptor userAccessInterceptor;
	@Getter
	@Setter
	private List enumLookupFqnForInterceptorParser;

	private HttpServer() {
	}

	@Builder
	private HttpServer(final String configPrefix, final String applicationName, final MapperFactory orikaFactory,
			final JsonMapper jsonMapper, final ExecutorService executorService, final String... appVersionFqns) {
		config = HttpServerConfiguration.read(configPrefix);
		this.applicationName = applicationName;
		this.executorService = executorService;
		this.appVersionFqns = new ArrayList<>(List.of(Optional.ofNullable(appVersionFqns).orElse(new String[0])));
		if (!this.appVersionFqns.contains(VERSION_FQN))
			this.appVersionFqns.add(VERSION_FQN);
		if (executorService == null) {
			this.executorService = new ThreadPoolExecutor(200, 200, 60L, TimeUnit.SECONDS,
					new LinkedBlockingQueue());
			((ThreadPoolExecutor) this.executorService).allowCoreThreadTimeOut(true);
		}
		if (applicationName == null)
			throw new IllegalArgumentException("The application-name cannot be null");
		this.jsonMapper = jsonMapper;
		if (this.jsonMapper == null)
			this.jsonMapper = JsonMapper.create();
		this.orikaFactory = orikaFactory;
		if (this.orikaFactory == null)
			this.orikaFactory = new DefaultMapperFactory.Builder().build();

		create();
	}

	public void addHandlerGroup(final HandlerGroup group) {
		handlerGroups.add(group);
	}

	public 

GenericHandlerGroupBuilder handlerGroupFor( final Class

jpaType, final Class jsonType, final CoreDaoProvider coreDaoProvider) { return new GenericHandlerGroupBuilder<>(this, jpaType, jsonType, coreDaoProvider.getCoreDao()); } private void create() { Server server = new Server(); ServerConnector connector = new ServerConnector(server); connector.setHost(config.host()); connector.setPort(config.port()); server.setConnectors(new ServerConnector[] { connector }); javalin = Javalin.create(c -> { c.server(() -> server) .accessManager(new HttpAccessManager(config.keycloakHost(), config.keycloakRealm())) .enableCorsForAllOrigins(); }).start(config.port()); javalin.before(ctx -> ctx.attribute(Attribute.JAVALIN_SERVER, this)); javalin.before(ctx -> ctx.attribute(Attribute.RESPONSE_TYPE, ResponseType.JSON)); javalin.before(ctx -> ctx.attribute(Attribute.RESPONSE_CONTENT_TYPE, "application/json")); javalin.before(this::unzip); javalin.after(this::render); javalin.error(404, ctx -> { ctx.result(jsonMapper.toStringFrom(MessageJson.builder() .message(NotFoundException.HTTP_STATUS + " " + NotFoundException.HTTP_TEXT) .build())).contentType("application/json").status(NotFoundException.HTTP_STATUS); }).exception(Exception.class, (e, ctx) -> { handleException(e, ctx); }); get("/", new AppNameHandler(applicationName)); get("/version", new AppVersionHandler(appVersionFqns)); get("/datetime", new DateTimeHandler()); get("/health", new HealthHandler()); get("/postman", new PostmanCollectionHandler()); registerShutdownHook(); } private void registerShutdownHook() { ShutdownHook.register(() -> { executorService.shutdown(); try { if (!executorService.awaitTermination(1, TimeUnit.SECONDS)) executorService.shutdownNow(); } catch (InterruptedException e) { executorService.shutdownNow(); } }); } private void handleException(final Exception e, final Context ctx) { int status; String message; if (HttpException.class.isAssignableFrom(e.getClass())) { HttpException h = (HttpException) e; status = h.getHttpStatus(); message = status + " " + h.getHttpText(); } else { status = 500; message = "500 Internal Server Error"; if (e instanceof UnauthorizedException) log.debug(e.getMessage()); else log.error(e.getMessage(), e); } ctx.result(jsonMapper.toStringFrom(MessageJson.builder().message(message).build())) .contentType("application/json") .status(status); } public void start() { for (HandlerGroup g : handlerGroups) g.addHandlers(this); for (HandlerInstance hi : handlerInstances) javalin.addHandler(hi.handlerType(), hi.path(), hi.handler(), hi.roles()); } public HttpServer get(final String path, final Handler handler, final Role... roles) { handlerInstances.add(HandlerInstance.builder() .path(path) .handlerType(HandlerType.GET) .handler(handler) .roles(new HashSet<>(Arrays.asList(roles))) .build()); return this; } public HttpServer put(final String path, final Handler handler, final Role... roles) { handlerInstances.add(HandlerInstance.builder() .path(path) .handlerType(HandlerType.PUT) .handler(handler) .roles(new HashSet<>(Arrays.asList(roles))) .build()); return this; } public HttpServer patch(final String path, final Handler handler, final Role... roles) { handlerInstances.add(HandlerInstance.builder() .path(path) .handlerType(HandlerType.PATCH) .handler(handler) .roles(new HashSet<>(Arrays.asList(roles))) .build()); return this; } public HttpServer post(final String path, final Handler handler, final Role... roles) { handlerInstances.add(HandlerInstance.builder() .path(path) .handlerType(HandlerType.POST) .handler(handler) .roles(new HashSet<>(Arrays.asList(roles))) .build()); return this; } public HttpServer delete(final String path, final Handler handler, final Role... roles) { handlerInstances.add(HandlerInstance.builder() .path(path) .handlerType(HandlerType.DELETE) .handler(handler) .roles(new HashSet<>(Arrays.asList(roles))) .build()); return this; } private void unzip(final Context ctx) throws IOException { String body = ctx.body(); if (body == null) return; if (ctx.header("Content-Encoding") == "gzip") { BufferedSource bs = Okio.buffer(Okio.source(ctx.bodyAsInputStream())); GzipSource gzipSource = new GzipSource(bs); body = Okio.buffer(gzipSource).readUtf8(); } ctx.bodyAsInputStream().reset(); ctx.attribute(Attribute.REQUEST_BODY, body); } private void render(final Context ctx) throws IOException { Object dto = ctx.attribute(Attribute.RESPONSE_OBJECT); if (dto != null) switch ((ResponseType) ctx.attribute(Attribute.RESPONSE_TYPE)) { case JSON: ctx.result(jsonMapper.toStringFrom(dto)); break; default: ctx.result((String) dto); } Integer status = ctx.attribute(Attribute.RESPONSE_STATUS); if (status != null) ctx.status(status); String contentType = ctx.attribute(Attribute.RESPONSE_CONTENT_TYPE); if (contentType != null) ctx.contentType(contentType); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy