
net.javapla.jawn.core.Jawn Maven / Gradle / Ivy
Show all versions of jawn-core Show documentation
package net.javapla.jawn.core;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.typesafe.config.Config;
import net.javapla.jawn.core.Plugin.Application;
import net.javapla.jawn.core.Server.ServerConfig;
import net.javapla.jawn.core.internal.Bootstrapper;
import net.javapla.jawn.core.internal.mvc.MvcRouteBuilder;
import net.javapla.jawn.core.internal.reflection.ClassLocator;
import net.javapla.jawn.core.internal.reflection.Reflection;
import net.javapla.jawn.core.util.StringUtil;
public class Jawn {
protected static final Logger log = LoggerFactory.getLogger(Jawn.class);
static final Logger SYS_ERROR_LOG = LoggerFactory.getLogger("JawnError");
private final Bootstrapper booter = new Bootstrapper(); // Core?
private final LinkedList routes = new LinkedList<>();
private final LinkedList mvcControllers = new LinkedList<>();
private final ServerConfig serverConfig = new ServerConfig();
public Jawn() {
}
/*protected Route.RouteBuilder get(final String path) {
return _route(HttpMethod.GET, path, (ctx) -> ctx.resp().respond(Status.OK));
}*/
protected Route.RouteBuilder get(final String path, final Route.Handler handler) {
return _route(HttpMethod.GET, path, handler);
}
/*protected Route.RouteBuilder get(final String path, final Route.NoResultHandler handler) {
return _route(HttpMethod.GET, path, handler);
}*/
protected Route.RouteBuilder get(final String path, final Route.ZeroArgHandler handler) {
return _route(HttpMethod.GET, path, handler);
}
protected Route.RouteBuilder get(final String path, Object result) {
return _route(HttpMethod.GET, path, (ctx) -> result).returnType(result.getClass());
}
protected Route.RouteBuilder head(final String path, final Route.Handler handler) {
return _route(HttpMethod.HEAD, path, handler);
}
protected Route.RouteBuilder post(final String path, final Route.Handler handler) {
return _route(HttpMethod.POST, path, handler);
}
/*protected Route.RouteBuilder post(final String path, final Route.NoResultHandler handler) {
return _route(HttpMethod.POST, path, handler);
}*/
protected Route.RouteBuilder put(final String path, final Route.Handler handler) {
return _route(HttpMethod.PUT, path, handler);
}
protected Route.RouteBuilder put(final String path, final Route.ZeroArgHandler handler) {
return _route(HttpMethod.PUT, path, handler);
}
protected Route.RouteBuilder delete(final String path, final Route.Handler handler) {
return _route(HttpMethod.DELETE, path, handler);
}
protected Route.RouteBuilder delete(final String path, final Route.ZeroArgHandler handler) {
return _route(HttpMethod.DELETE, path, handler);
}
protected Route.Builder _route(HttpMethod method, final String path, final Route.Handler handler) {
Route.Builder bob = new Route.Builder(method, _pathPrefix(path), handler);
routes.add(bob);
return bob;
}
protected Jawn routes(Runnable routes) {
routes.run();
return this;
}
/**
* path("/api/v1/", () -> {
* get("/{id}", ctx -> ... );
* get("/", ctx -> ... );
* post("/", ctx -> ... );
* });
* @return
*/
protected Jawn path(final String rootPath, final Runnable routes) {
pathPrefix.addLast(rootPath);
routes(routes);
pathPrefix.removeLast();
return this;
}
private final LinkedList pathPrefix = new LinkedList<>();
private String _pathPrefix(String path) {
return pathPrefix.stream().collect(Collectors.joining("","",path));
}
protected Route.RouteBuilder ws(final String path, WebSocket.Initialiser initialiser) {
// Only GET is supported to start the handshake
return _route(HttpMethod.GET, path, new WebSocket.WebSocketHandler(initialiser)).returnType(Context.class);
}
/* ******************************************
* MVC
* ****************************************** */
protected Route.RouteBuilder controller(final Class> controller) {
MvcRouteBuilder bob = new MvcRouteBuilder(controller);
mvcControllers.add(bob);
return bob;
}
protected Route.RouteBuilder controller(Object controller) {
MvcRouteBuilder bob = new MvcRouteBuilder(controller);
mvcControllers.add(bob);
return bob;
}
protected void controllers(String packageToScan) {
ClassLocator.list(packageToScan, booter.classLoader()).forEach(this::controller);
}
protected void controllers(Package packageToScan) {
controllers(packageToScan.getName());
}
/**
* Look for MVC controllers at the standard package by convention.
*
* I.e.: The 'controllers' package next to this implementor of {@link Jawn}
*
*
* This is just a short-hand for controllers(this.getClass().getPackageName() + ".controllers");
*/
protected void controllers() {
controllers(this.getClass().getPackageName() + ".controllers");
}
protected void install(Plugin plugin) {
//Server service = loader.findFirst().get();
//registry.register(Server.class, server);
// TODO
booter.install(plugin);
}
protected ServerConfig server() {
return serverConfig;
}
/* ******************************************
* Registry
* ****************************************** */
protected T require(Class type) {
return booter.registry().require(type);
}
protected void register(Object instance) {
booter.registry().register(instance);
}
/* ******************************************
* Life cycle
* ****************************************** */
protected Jawn onStartup(Runnable task) {
booter.onStartup(task);
return this;
}
protected Jawn onShutdown(Runnable task) {
booter.onShutdown(task);
return this;
}
/**
* Running tasks when the server definitely is ready to receive
*/
protected Jawn onServerStarted(Runnable task) {
booter.onServerStarted(task);
return this;
}
public void start() {
long startupTime = System.currentTimeMillis();
// shutdown hook
Runtime.getRuntime().addShutdownHook(new Thread(this::stop));
// find server
ServiceLoader loader = ServiceLoader.load(Server.class);
Optional server = loader.findFirst();
if (server.isEmpty()) {
throw new IllegalStateException("Server not found");
}
// bootstrap
//bootstrap.boot(mode, serverConfig, sessionConfig.sessionStore, this::buildRoutes);
Application moduleConfig = booter.boot(this::buildRoutes);
// TODO ServiceLoader.load Registries after everything else is loaded
// start server
Config config = booter.config().hasPath("server") ? booter.config().getConfig("server") : null;
serverConfig.config(config);
try {
server.get().start(serverConfig, moduleConfig);
} catch (Exception e) {
e.printStackTrace();
stop();
return;
}
// Signal server has started
booter.serverStarted();
//log.info(FrameworkBootstrap.FRAMEWORK_SPLASH);
log.info("Bootstrap of framework started in: " + (System.currentTimeMillis() - startupTime) + " ms");
//log.info("Jawn: Environment: " + mode.name());
log.info("Jawn: Running on port: " + serverConfig.port());
}
public void stop() {
booter.shutdown();
}
public static final void run(final String ... args) {
// TODO
// do some command line parsing of the args
// port=8080 mode=prod
Class caller = Reflection.callingClass(Jawn.class);
if (caller == null) {
log.error("Could not determine a class extending {}, and therefore not able to start a server", Jawn.class);
return;
}
try {
/*Jawn instance = null;
if (args.length > 0) {
try {
instance = caller.getDeclaredConstructor(String[].class).newInstance(args);
} catch (NoSuchMethodException nothing) {}
}
if (instance == null)
instance = caller.getDeclaredConstructor().newInstance();*/
Jawn instance = caller.getDeclaredConstructor().newInstance();
instance.start();
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
log.error("", e);
}
}
static Map commandLineParsing(String ... args) {
if (args == null || args.length == 0) return Collections.emptyMap();
LinkedHashMap map = new LinkedHashMap<>();
StringUtil.split(args, '=', map::put);
return map;
}
private Stream buildRoutes(Registry registry) {
// Execute / instantiate controllers after all other potential classes have been created and are available
mvcControllers.forEach(bob -> routes.addAll(bob.build(registry)));
return routes.stream();
}
}