package org.webpieces.router.impl;
import java.util.concurrent.CompletableFuture;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.webpieces.ctx.api.RequestContext;
import org.webpieces.ctx.api.RouterRequest;
import org.webpieces.router.api.ResponseStreamer;
import org.webpieces.router.api.RouterService;
import org.webpieces.router.api.actions.Action;
import org.webpieces.router.api.dto.MethodMeta;
import org.webpieces.router.api.exceptions.NotFoundException;
import org.webpieces.router.impl.hooks.ClassForName;
import org.webpieces.router.impl.loader.HaveRouteException;
import org.webpieces.router.impl.loader.ProdClassForName;
import org.webpieces.router.impl.model.MatchResult;
import org.webpieces.router.impl.params.ObjectTranslator;
import org.webpieces.util.filters.Service;
import org.webpieces.util.logging.Logger;
import org.webpieces.util.logging.LoggerFactory;
@Singleton
public class ProdRouterService extends AbstractRouterService implements RouterService {
private static final Logger log = LoggerFactory.getLogger(ProdRouterService.class);
private RouteLoader routeLoader;
private ClassForName loader;
@Inject
public ProdRouterService(
RouteLoader routeLoader,
CookieTranslator cookieTranslator,
ObjectTranslator translator,
ProdClassForName loader
) {
super(routeLoader, cookieTranslator, translator);
this.routeLoader = routeLoader;
this.loader = loader;
}
//add Route HOOK callback so translate RouteId -> route and route->controller.method to call
@Override
public void start() {
log.info("Starting PROD server with NO compiling classloader");
routeLoader.load(loader, injector -> runStartupHooks(injector));
started = true;
}
@Override
public void stop() {
started = false;
}
@Override
public CompletableFuture<Void> incomingRequestImpl(RequestContext ctx, ResponseStreamer responseCb) {
MatchResult result = fetchRoute(ctx);
CompletableFuture<Void> future;
try {
ProdErrorRoutes errorRoutes = new ProdErrorRoutes(ctx.getRequest(), routeLoader);
future = routeLoader.invokeRoute(result, ctx, responseCb, errorRoutes);
} catch(Throwable e) {
future = new CompletableFuture<Void>();
future.completeExceptionally(e);
}
return future.exceptionally( t -> {
throw new HaveRouteException(result, t);
});
}
//This only exists so dev mode can swap it out and load error routes dynamically as code changes..
private static class ProdErrorRoutes implements ErrorRoutes {
private RouteLoader routeLoader;
private RouterRequest req;
public ProdErrorRoutes(RouterRequest req, RouteLoader routeLoader) {
this.req = req;
this.routeLoader = routeLoader;
}
public NotFoundInfo fetchNotfoundRoute(NotFoundException e) {
//not found is normal in prod mode so we don't log that and only log warnings in dev mode
RouteMeta result = routeLoader.fetchNotFoundRoute(req.domain);
//every request for not found route must apply filters(unlike other routes). There are tests
//for this use case with the LoginFitler in TestHttps
Service<MethodMeta, Action> svc = routeLoader.createNotFoundService(result, req);
return new NotFoundInfo(result, svc, req);
}
public RouteMeta fetchInternalServerErrorRoute() {
return routeLoader.fetchInternalErrorRoute(req.domain);
}
}
@Override
protected ErrorRoutes getErrorRoutes(RequestContext ctx) {
return new ProdErrorRoutes(ctx.getRequest(), routeLoader);
}
}