From 7bf6e183ff8abd0c35eeb29f399da12389562ecb Mon Sep 17 00:00:00 2001 From: James Moger Date: Thu, 21 Nov 2013 18:32:21 -0500 Subject: Moved servlets and services to separate packages Change-Id: I5f0f50f4ae7d332e9f724a2e6f074fa71f646035 --- .../java/com/gitblit/servlet/GitblitContext.java | 432 +++++++++++++++++++++ 1 file changed, 432 insertions(+) create mode 100644 src/main/java/com/gitblit/servlet/GitblitContext.java (limited to 'src/main/java/com/gitblit/servlet/GitblitContext.java') diff --git a/src/main/java/com/gitblit/servlet/GitblitContext.java b/src/main/java/com/gitblit/servlet/GitblitContext.java new file mode 100644 index 00000000..73250121 --- /dev/null +++ b/src/main/java/com/gitblit/servlet/GitblitContext.java @@ -0,0 +1,432 @@ +/* + * Copyright 2011 gitblit.com. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gitblit.servlet; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.servlet.ServletContext; +import javax.servlet.annotation.WebListener; + +import com.gitblit.Constants; +import com.gitblit.DaggerModule; +import com.gitblit.FileSettings; +import com.gitblit.IStoredSettings; +import com.gitblit.Keys; +import com.gitblit.WebXmlSettings; +import com.gitblit.dagger.DaggerContextListener; +import com.gitblit.git.GitServlet; +import com.gitblit.manager.IFederationManager; +import com.gitblit.manager.IGitblitManager; +import com.gitblit.manager.IManager; +import com.gitblit.manager.INotificationManager; +import com.gitblit.manager.IProjectManager; +import com.gitblit.manager.IRepositoryManager; +import com.gitblit.manager.IRuntimeManager; +import com.gitblit.manager.IServicesManager; +import com.gitblit.manager.ISessionManager; +import com.gitblit.manager.IUserManager; +import com.gitblit.utils.ContainerUtils; +import com.gitblit.utils.StringUtils; +import com.gitblit.wicket.GitblitWicketFilter; + +import dagger.ObjectGraph; + +/** + * This class is the main entry point for the entire webapp. It is a singleton + * created manually by Gitblit GO or dynamically by the WAR/Express servlet + * container. This class instantiates and starts all managers followed by + * instantiating and registering all servlets and filters. + * + * Leveraging Servlet 3 and Dagger static dependency injection allows Gitblit to + * be modular and completely code-driven rather then relying on the fragility of + * a web.xml descriptor and the static & monolithic design previously used. + * + * @author James Moger + * + */ +@WebListener +public class GitblitContext extends DaggerContextListener { + + private static GitblitContext gitblit; + + private final List managers = new ArrayList(); + + private final IStoredSettings goSettings; + + private final File goBaseFolder; + + /** + * Construct a Gitblit WAR/Express context. + */ + public GitblitContext() { + this.goSettings = null; + this.goBaseFolder = null; + gitblit = this; + } + + /** + * Construct a Gitblit GO context. + * + * @param settings + * @param baseFolder + */ + public GitblitContext(IStoredSettings settings, File baseFolder) { + this.goSettings = settings; + this.goBaseFolder = baseFolder; + gitblit = this; + } + + /** + * This method is only used for unit and integration testing. + * + * @param managerClass + * @return a manager + */ + @SuppressWarnings("unchecked") + public static X getManager(Class managerClass) { + for (IManager manager : gitblit.managers) { + if (managerClass.isAssignableFrom(manager.getClass())) { + return (X) manager; + } + } + return null; + } + + /** + * Returns Gitblit's Dagger injection modules. + */ + @Override + protected Object [] getModules() { + return new Object [] { new DaggerModule() }; + } + + /** + * Prepare runtime settings and start all manager instances. + */ + @Override + protected void beforeServletInjection(ServletContext context) { + ObjectGraph injector = getInjector(context); + + // create the runtime settings object + IStoredSettings runtimeSettings = injector.get(IStoredSettings.class); + final File baseFolder; + + if (goSettings != null) { + // Gitblit GO + baseFolder = configureGO(context, goSettings, goBaseFolder, runtimeSettings); + } else { + // servlet container + WebXmlSettings webxmlSettings = new WebXmlSettings(context); + String contextRealPath = context.getRealPath("/"); + File contextFolder = (contextRealPath != null) ? new File(contextRealPath) : null; + + if (!StringUtils.isEmpty(System.getenv("OPENSHIFT_DATA_DIR"))) { + // RedHat OpenShift + baseFolder = configureExpress(context, webxmlSettings, contextFolder, runtimeSettings); + } else { + // standard WAR + baseFolder = configureWAR(context, webxmlSettings, contextFolder, runtimeSettings); + } + + // Test for Tomcat forward-slash/%2F issue and auto-adjust settings + ContainerUtils.CVE_2007_0450.test(runtimeSettings); + } + + // Manually configure IRuntimeManager + logManager(IRuntimeManager.class); + IRuntimeManager runtime = injector.get(IRuntimeManager.class); + runtime.setBaseFolder(baseFolder); + runtime.getStatus().isGO = goSettings != null; + runtime.getStatus().servletContainer = context.getServerInfo(); + runtime.start(); + managers.add(runtime); + + // start all other managers + startManager(injector, INotificationManager.class); + startManager(injector, IUserManager.class); + startManager(injector, ISessionManager.class); + startManager(injector, IRepositoryManager.class); + startManager(injector, IProjectManager.class); + startManager(injector, IGitblitManager.class); + startManager(injector, IFederationManager.class); + startManager(injector, IServicesManager.class); + + logger.info(""); + logger.info("All managers started."); + logger.info(""); + } + + protected X startManager(ObjectGraph injector, Class clazz) { + logManager(clazz); + X x = injector.get(clazz); + x.start(); + managers.add(x); + return x; + } + + protected void logManager(Class clazz) { + logger.info(""); + logger.info("----[{}]----", clazz.getName()); + } + + /** + * Instantiate and inject all filters and servlets into the container using + * the servlet 3 specification. + */ + @Override + protected void injectServlets(ServletContext context) { + // access restricted servlets + serve(context, Constants.GIT_PATH, GitServlet.class, GitFilter.class); + serve(context, Constants.PAGES, PagesServlet.class, PagesFilter.class); + serve(context, Constants.RPC_PATH, RpcServlet.class, RpcFilter.class); + serve(context, Constants.ZIP_PATH, DownloadZipServlet.class, DownloadZipFilter.class); + serve(context, Constants.SYNDICATION_PATH, SyndicationServlet.class, SyndicationFilter.class); + + // servlets + serve(context, Constants.FEDERATION_PATH, FederationServlet.class); + serve(context, Constants.SPARKLESHARE_INVITE_PATH, SparkleShareInviteServlet.class); + serve(context, Constants.BRANCH_GRAPH_PATH, BranchGraphServlet.class); + file(context, "/robots.txt", RobotsTxtServlet.class); + file(context, "/logo.png", LogoServlet.class); + + // optional force basic authentication + filter(context, "/*", EnforceAuthenticationFilter.class, null); + + // Wicket + String toIgnore = StringUtils.flattenStrings(getRegisteredPaths(), ","); + Map params = new HashMap(); + params.put(GitblitWicketFilter.FILTER_MAPPING_PARAM, "/*"); + params.put(GitblitWicketFilter.IGNORE_PATHS_PARAM, toIgnore); + filter(context, "/*", GitblitWicketFilter.class, params); + } + + /** + * Gitblit is being shutdown either because the servlet container is + * shutting down or because the servlet container is re-deploying Gitblit. + */ + @Override + protected void destroyContext(ServletContext context) { + logger.info("Gitblit context destroyed by servlet container."); + for (IManager manager : managers) { + logger.debug("stopping {}", manager.getClass().getSimpleName()); + manager.stop(); + } + } + + /** + * Configures Gitblit GO + * + * @param context + * @param settings + * @param baseFolder + * @param runtimeSettings + * @return the base folder + */ + protected File configureGO( + ServletContext context, + IStoredSettings goSettings, + File goBaseFolder, + IStoredSettings runtimeSettings) { + + logger.debug("configuring Gitblit GO"); + + // merge the stored settings into the runtime settings + // + // if runtimeSettings is also a FileSettings w/o a specified target file, + // the target file for runtimeSettings is set to "localSettings". + runtimeSettings.merge(goSettings); + File base = goBaseFolder; + return base; + } + + + /** + * Configures a standard WAR instance of Gitblit. + * + * @param context + * @param webxmlSettings + * @param contextFolder + * @param runtimeSettings + * @return the base folder + */ + protected File configureWAR( + ServletContext context, + WebXmlSettings webxmlSettings, + File contextFolder, + IStoredSettings runtimeSettings) { + + // Gitblit is running in a standard servlet container + logger.debug("configuring Gitblit WAR"); + logger.info("WAR contextFolder is " + ((contextFolder != null) ? contextFolder.getAbsolutePath() : "")); + + String path = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data"); + + if (path.contains(Constants.contextFolder$) && contextFolder == null) { + // warn about null contextFolder (issue-199) + logger.error(""); + logger.error(MessageFormat.format("\"{0}\" depends on \"{1}\" but \"{2}\" is returning NULL for \"{1}\"!", + Constants.baseFolder, Constants.contextFolder$, context.getServerInfo())); + logger.error(MessageFormat.format("Please specify a non-parameterized path for {0} in web.xml!!", Constants.baseFolder)); + logger.error(MessageFormat.format("OR configure your servlet container to specify a \"{0}\" parameter in the context configuration!!", Constants.baseFolder)); + logger.error(""); + } + + try { + // try to lookup JNDI env-entry for the baseFolder + InitialContext ic = new InitialContext(); + Context env = (Context) ic.lookup("java:comp/env"); + String val = (String) env.lookup("baseFolder"); + if (!StringUtils.isEmpty(val)) { + path = val; + } + } catch (NamingException n) { + logger.error("Failed to get JNDI env-entry: " + n.getExplanation()); + } + + File base = com.gitblit.utils.FileUtils.resolveParameter(Constants.contextFolder$, contextFolder, path); + base.mkdirs(); + + // try to extract the data folder resource to the baseFolder + File localSettings = new File(base, "gitblit.properties"); + if (!localSettings.exists()) { + extractResources(context, "/WEB-INF/data/", base); + } + + // delegate all config to baseFolder/gitblit.properties file + FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath()); + + // merge the stored settings into the runtime settings + // + // if runtimeSettings is also a FileSettings w/o a specified target file, + // the target file for runtimeSettings is set to "localSettings". + runtimeSettings.merge(fileSettings); + + return base; + } + + /** + * Configures an OpenShift instance of Gitblit. + * + * @param context + * @param webxmlSettings + * @param contextFolder + * @param runtimeSettings + * @return the base folder + */ + private File configureExpress( + ServletContext context, + WebXmlSettings webxmlSettings, + File contextFolder, + IStoredSettings runtimeSettings) { + + // Gitblit is running in OpenShift/JBoss + logger.debug("configuring Gitblit Express"); + String openShift = System.getenv("OPENSHIFT_DATA_DIR"); + File base = new File(openShift); + logger.info("EXPRESS contextFolder is " + contextFolder.getAbsolutePath()); + + // Copy the included scripts to the configured groovy folder + String path = webxmlSettings.getString(Keys.groovy.scriptsFolder, "groovy"); + File localScripts = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, path); + if (!localScripts.exists()) { + File warScripts = new File(contextFolder, "/WEB-INF/data/groovy"); + if (!warScripts.equals(localScripts)) { + try { + com.gitblit.utils.FileUtils.copy(localScripts, warScripts.listFiles()); + } catch (IOException e) { + logger.error(MessageFormat.format( + "Failed to copy included Groovy scripts from {0} to {1}", + warScripts, localScripts)); + } + } + } + + // merge the WebXmlSettings into the runtime settings (for backwards-compatibilty) + runtimeSettings.merge(webxmlSettings); + + // settings are to be stored in openshift/gitblit.properties + File localSettings = new File(base, "gitblit.properties"); + FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath()); + + // merge the stored settings into the runtime settings + // + // if runtimeSettings is also a FileSettings w/o a specified target file, + // the target file for runtimeSettings is set to "localSettings". + runtimeSettings.merge(fileSettings); + + return base; + } + + protected void extractResources(ServletContext context, String path, File toDir) { + for (String resource : context.getResourcePaths(path)) { + // extract the resource to the directory if it does not exist + File f = new File(toDir, resource.substring(path.length())); + if (!f.exists()) { + InputStream is = null; + OutputStream os = null; + try { + if (resource.charAt(resource.length() - 1) == '/') { + // directory + f.mkdirs(); + extractResources(context, resource, f); + } else { + // file + f.getParentFile().mkdirs(); + is = context.getResourceAsStream(resource); + os = new FileOutputStream(f); + byte [] buffer = new byte[4096]; + int len = 0; + while ((len = is.read(buffer)) > -1) { + os.write(buffer, 0, len); + } + } + } catch (FileNotFoundException e) { + logger.error("Failed to find resource \"" + resource + "\"", e); + } catch (IOException e) { + logger.error("Failed to copy resource \"" + resource + "\" to " + f, e); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + // ignore + } + } + if (os != null) { + try { + os.close(); + } catch (IOException e) { + // ignore + } + } + } + } + } + } +} -- cgit v1.2.3