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

io.metaloom.test.container.provider.server.ServerApi Maven / Gradle / Ivy

There is a newer version: 0.1.4
Show newest version
package io.metaloom.test.container.provider.server;

import java.sql.SQLException;
import java.util.concurrent.atomic.AtomicReference;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.metaloom.test.container.provider.DatabaseAllocation;
import io.metaloom.test.container.provider.DatabasePool;
import io.metaloom.test.container.provider.DatabasePoolManager;
import io.metaloom.test.container.provider.DatabaseSettings;
import io.metaloom.test.container.provider.model.DatabaseAllocationResponse;
import io.metaloom.test.container.provider.model.DatabasePoolConnection;
import io.metaloom.test.container.provider.model.DatabasePoolListResponse;
import io.metaloom.test.container.provider.model.DatabasePoolRequest;
import io.metaloom.test.container.provider.model.DatabasePoolResponse;
import io.metaloom.test.container.provider.model.DatabasePoolSettings;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.sockjs.SockJSSocket;

@Singleton
public class ServerApi {

	private static final Logger log = LoggerFactory.getLogger(ServerApi.class);

	private final DatabasePoolManager manager;

	@Inject
	public ServerApi(DatabasePoolManager manager) {
		this.manager = manager;
	}

	public void poolDeleteHandler(RoutingContext rc) {
		String id = rc.pathParam("id");
		log.info("Deleting pool {}", id);
		boolean hasPool = manager.contains(id);
		if (!hasPool) {
			rc.response().setStatusCode(404).end();
			return;
		}
		boolean result = manager.deletePool(id);
		if (result) {
			log.info("Pool {} deleted", id);
			rc.response().setStatusCode(204).end();
			return;
		} else {
			log.error("Error while deleting pool {}", id);
			rc.response().setStatusCode(400).end();
		}
	}

	public void upsertPoolHandler(RoutingContext rc) {
		String id = rc.pathParam("id");
		DatabasePoolRequest model = rc.body().asPojo(DatabasePoolRequest.class);
		String templateDatabaseName = require(model.getTemplateDatabaseName(), "templateDatabaseName");

		DatabasePool pool = manager.getPool(id);
		if (pool == null) {
			DatabasePoolConnection connection = model.getConnection();
			DatabasePoolSettings settings = model.getSettings();
			log.info("Adding pool {}", id);

			require(connection, "connection");
			String host = require(connection.getHost(), "host");
			Integer port = require(connection.getPort(), "port");
			String internalHost = connection.getInternalHost() == null ? host : connection.getInternalHost();
			Integer internalPort = connection.getInternalPort() == null ? port : connection.getInternalPort();
			String username = require(connection.getUsername(), "username");
			String password = require(connection.getPassword(), "password");
			String adminDB = require(connection.getDatabase(), "database");
			pool = manager.createPool(id, host, port, internalHost, internalPort, username, password, adminDB);

			// Apply the custom setting if those were provided. Otherwise the manager will use the default values.
			if (settings != null) {
				Integer minimum = settings.getMinimum();
				if (minimum != null) {
					pool.setMinimum(minimum);
				}
				Integer maximum = settings.getMaximum();
				if (maximum != null) {
					pool.setMaximum(maximum);
				}
				Integer increment = settings.getIncrement();
				if (increment != null) {
					pool.setIncrement(increment);
				}
			}
			pool.setTemplateDatabaseName(templateDatabaseName);
			pool.start();
		} else {
			log.info("Recreating pool {}", id);
			DatabaseSettings settings = pool.settings();
			manager.deletePool(id);
			DatabasePool newPool = manager.createPool(id, settings.host(), settings.port(), settings.internalHost(), settings.internalPort(),
				settings.username(), settings.password(), settings.adminDB(), templateDatabaseName);
			if (!newPool.isStarted()) {
				newPool.start();
			}
			pool = newPool;
		}

		DatabasePoolResponse response = ModelHelper.toModel(pool);
		rc.response().putHeader(HttpHeaders.CONTENT_TYPE, "application/json").end(JSON.toBuffer(response));

	}

	private  T require(T value, String key) {
		if (value == null) {
			throw new ServerError(String.format("Field %s is missing", key));
		}
		return value;
	}

	public void failureHandler(RoutingContext rc) {
		if (rc.failed() && rc.failure() instanceof ServerError se) {
			rc.response()
				.setStatusCode(400)
				.putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
				.end(se.toJson().toBuffer());
			return;
		}
		log.error("Error while handling request for " + rc.normalizedPath(), rc.failure());
		rc.next();
	}

	public void loadPoolHandler(RoutingContext rc) {
		String id = rc.pathParam("id");
		DatabasePool pool = manager.getPool(id);
		if (pool == null) {
			log.info("Pool {} not found", id);
			rc.response().setStatusCode(404).end();
		} else {
			DatabasePoolResponse response = ModelHelper.toModel(pool);
			rc.response().putHeader(HttpHeaders.CONTENT_TYPE, "application/json").end(JSON.toBuffer(response));
		}
	}

	public void listPoolsHandler(RoutingContext rc) {
		log.info("Getting stat request");

		DatabasePoolListResponse response = new DatabasePoolListResponse();
		for (DatabasePool pool : manager.getPools()) {
			response.add(ModelHelper.toModel(pool));
		}

		Buffer buffer = JSON.toBuffer(response);
		rc.response().putHeader(HttpHeaders.CONTENT_TYPE, "application/json").end(buffer);
	}

	public void websocketHandler(SockJSSocket sock) {

		AtomicReference allocationRef = new AtomicReference<>();

		// Check the message - currently msgs will only be send from the client to request a new database
		sock.handler(msg -> {
			ProviderRequest providerRequest = ProviderRequest.from(msg.toString());

			log.info("Allocating db for {}", providerRequest);
			try {
				DatabasePool pool = manager.getPool(providerRequest.poolId());
				if (pool == null) {
					log.error("Unable to fullfil request. Provided pool for id {} not found", providerRequest.poolId());
					error(sock, "Pool not found {" + providerRequest.poolId() + "}");
					return;
				}
				DatabaseAllocation allocation = pool.allocate(providerRequest.testName());
				if (allocation == null) {
					error(sock, "Unable to allocate database from pool {" + providerRequest.poolId() + "}");
					return;
				}
				allocationRef.set(allocation);
				DatabaseAllocationResponse response = ModelHelper.toModel(allocation);
				sock.write(JsonObject.mapFrom(response).toBuffer());
			} catch (SQLException e) {
				log.error("Error while allocating database for test {}", providerRequest, e);
				error(sock, "Unknown error");
			}
		});
		sock.closeHandler(close -> {
			DatabaseAllocation allocation = allocationRef.get();
			if (allocation != null) {
				log.info("Releasing allocation {}", allocation);
				try {
					allocation.release();
				} catch (Exception e) {
					log.error("Error while releasing database for test {}", allocation.db().name(), e);
				}
			} else {
				log.debug("No allocation found. Just closing connection.");
			}
		});
	}

	private void error(SockJSSocket sock, String msg) {
		sock.write(new JsonObject().put("error", msg).toBuffer());
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy