package org.gcube.smartgears; import static java.util.Collections.emptyList; import java.util.Set; import jakarta.servlet.ServletContainerInitializer; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import org.gcube.smartgears.context.application.ApplicationContext; import org.gcube.smartgears.context.container.ContainerContext; import org.gcube.smartgears.managers.ApplicationManager; import org.gcube.smartgears.managers.ContainerManager; import org.gcube.smartgears.provider.ProviderFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics; import io.micrometer.core.instrument.binder.jvm.JvmGcMetrics; import io.micrometer.core.instrument.binder.jvm.JvmInfoMetrics; import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics; import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics; import io.micrometer.core.instrument.binder.logging.LogbackMetrics; import io.micrometer.core.instrument.binder.system.ProcessorMetrics; import io.micrometer.core.instrument.binder.system.UptimeMetrics; import io.micrometer.core.instrument.binder.tomcat.TomcatMetrics; import io.micrometer.prometheus.PrometheusConfig; import io.micrometer.prometheus.PrometheusMeterRegistry; /** * Bootstraps management of all deployed applications which require it. * * @author Fabio Simeoni * */ public class Bootstrap implements ServletContainerInitializer { private static Logger log = LoggerFactory.getLogger(Bootstrap.class); private static boolean smartgearsHasStarted = false; private static boolean containerHasFailed = false; private static ContainerManager manager; private static ContainerContext context; public Bootstrap() { log.info("bootstrap started the container"); if (smartgearsHasStarted) return; smartgearsHasStarted = true; initialiseContainer(); //this can fail the app: managed resources need a working container startContainerIfItHasntAlreadyFailed(); } @Override public void onStartup(Set> c, ServletContext application) throws ServletException { ApplicationManager appManager = new ApplicationManager(); log.info("check if is managed @ {}", application.getContextPath()); //act only on resources if (isResource(application)) { try { log.info("starting management of application @ {}", application.getContextPath()); ApplicationContext app = appManager.start(context, application); manager.manage(app); context.configuration().app(app.configuration()); } catch (Throwable t) { appManager.stop(); throw new ServletException("cannot manage application @ " + application.getContextPath() + " (see cause)", t); } } else log.info("is not managed @ {}", application.getContextPath()); } // helpers @SuppressWarnings("resource") private void initialiseContainer() { ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); try { // TODO Ask why is needed? Thread.currentThread().setContextClassLoader(ContainerManager.class.getClassLoader()); log.trace("smartgears is starting"); /* Get the ContainerContext. Look at DefaultProvider */ context = ProviderFactory.provider().containerContext(); PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT); new ClassLoaderMetrics().bindTo(registry); new JvmMemoryMetrics().bindTo(registry); new JvmGcMetrics().bindTo(registry); new ProcessorMetrics().bindTo(registry); new JvmThreadMetrics().bindTo(registry); new UptimeMetrics().bindTo(registry); new ProcessorMetrics().bindTo(registry); new TomcatMetrics(null, emptyList()).bindTo(registry); new LogbackMetrics().bindTo(registry); new JvmInfoMetrics().bindTo(registry); Metrics.addRegistry(registry); /* Validate the configuration retrieved by ContainerContext * using gcube facilities annotation based * ( i.e org.gcube.common.validator.annotations) */ context.configuration().validate(); } catch (RuntimeException e) { containerHasFailed = true; log.error("cannot start smartgears", e); //we let the container continue } finally {//restore the classloader of the current application Thread.currentThread().setContextClassLoader(contextCL); } } private void startContainerIfItHasntAlreadyFailed() { if (containerHasFailed) throw new IllegalStateException("container is not managed due to previous failure"); ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); // we initialise the container in the same classloader as this // lib, lest container bind its resources to the current webapp try { // TODO Ask why is needed? Thread.currentThread().setContextClassLoader(ContainerManager.class.getClassLoader()); manager = ContainerManager.instance; context = manager.start(context); } catch (RuntimeException e) { containerHasFailed = true; throw new IllegalStateException("cannot manage container", e); } finally {//restore the classloader of the current application Thread.currentThread().setContextClassLoader(contextCL); } } private boolean isResource(ServletContext application) { //with care: smartgears may have already failed at this stage but we want to recognise //apps that would have been managed otherwise and give specific errors for those return (!containerHasFailed && context.configuration().app(application.getContextPath())!=null) || application.getResourceAsStream(Constants.configuration_file_path) != null; } }