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

org.webpieces.devrouter.impl.DevRoutingService Maven / Gradle / Ivy

Go to download

Library that swaps out specific http-router components to be able to compile code on any request that has changed for use in development servers

There is a newer version: 2.1.109
Show newest version
package org.webpieces.devrouter.impl;

import java.util.function.Consumer;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.webpieces.compiler.api.ClassFileNotFoundException;
import org.webpieces.ctx.api.RequestContext;
import org.webpieces.router.api.RouterConfig;
import org.webpieces.router.api.routes.WebAppMeta;
import org.webpieces.router.impl.AbstractRouterService;
import org.webpieces.router.impl.CookieTranslator;
import org.webpieces.router.impl.RouteLoader;
import org.webpieces.router.impl.WebInjector;
import org.webpieces.router.impl.params.ObjectTranslator;
import org.webpieces.router.impl.proxyout.ProxyStreamHandle;
import org.webpieces.router.impl.routeinvoker.RouterStreamRef;
import org.webpieces.router.impl.routers.ARouter;
import org.webpieces.util.cmdline2.Arguments;
import org.webpieces.util.file.VirtualFile;

import com.google.inject.Injector;

@Singleton
public class DevRoutingService extends AbstractRouterService {

	private static final Logger log = LoggerFactory.getLogger(DevRoutingService.class);
	private static final Consumer NO_OP = whatever -> {};
	
	private long lastFileTimestamp;
	private RouteLoader routeLoader;
	private DevClassForName classLoader;
	private WebAppMeta routerModule;
	private RouterConfig config;
	private ARouter router;
	private Arguments arguments;

	@Inject
	public DevRoutingService(
			RouteLoader routeConfig, 
			RouterConfig config, 
			ARouter router, 
			DevClassForName loader, 
			CookieTranslator cookieTranslator,
			ObjectTranslator objTranslator,
			WebInjector webInjector
	) {
		super(webInjector, routeConfig, cookieTranslator, objTranslator);
		this.routeLoader = routeConfig;
		this.config = config;
		this.router = router;
		this.classLoader = loader;
		this.lastFileTimestamp = config.getMetaFile().lastModified();
	}

	@Override
	public void configure(Arguments arguments) {
		this.arguments = arguments;
		routeLoader.configure(classLoader, arguments);
	}
	
	@Override
	public Injector start() {
		log.info("Starting DEVELOPMENT server with CompilingClassLoader and HotSwap");
		Injector inj = loadOrReload(injector -> runStartupHooks(injector));
		return inj;
	}

	@Override
	public RouterStreamRef incomingRequestImpl(RequestContext ctx, ProxyStreamHandle handler) {
		//In DevRouter, check if we need to reload the text file as it points to a new RouterModules.java implementation file
		boolean reloaded = reloadIfTextFileChanged();
		
		if(!reloaded) {
			try {
				reloadIfClassFilesChanged();
			} catch(Throwable exc) {
				ctx.getRequest().requestState.put(DevRouteInvoker.ERROR_KEY, exc);
				return router.invoke(ctx, handler);
			}
		}
		
		return router.invoke(ctx, handler);
	}

	private boolean reloadIfTextFileChanged() {
		VirtualFile metaTextFile = config.getMetaFile();
		//if timestamp the same, no changes
		if(lastFileTimestamp == metaTextFile.lastModified())
			return false;

		log.info("text file changed so need to reload RouterModules.java implementation");

		routerModule = routeLoader.configure(classLoader, arguments);
		routeLoader.load(injector -> runStartupHooks(injector));
		lastFileTimestamp = metaTextFile.lastModified();
		return true;
	}

	private void reloadIfClassFilesChanged() {
		String routerModuleClassName = routerModule.getClass().getName();
		ClassLoader previousCl = routerModule.getClass().getClassLoader();
		
		Class newClazz = classLoader.clazzForName(routerModuleClassName);
		ClassLoader newClassLoader = newClazz.getClassLoader();
		if(previousCl == newClassLoader)
			return;
		
		log.info("classloader change so we need to reload all router classes");
		loadOrReload(injector -> runStartupHooks(injector));
	}

	private Injector loadOrReload(Consumer startupHook) {
		routerModule = routeLoader.configure(classLoader, arguments);
		try {
			return routeLoader.load(startupHook);
		} catch ( ClassFileNotFoundException e) {
			throw new ClassFileNotFoundException("Check your routes files as likely they are loading a non-existent controller", e);
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy