PageRenderTime 1660ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/com/gitblit/servlet/GitblitContext.java

https://gitlab.com/tigefa/gitblit
Java | 511 lines | 319 code | 64 blank | 128 comment | 37 complexity | 730c2d0349870accf4b2012264e2b532 MD5 | raw file
  1. /*
  2. * Copyright 2011 gitblit.com.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.gitblit.servlet;
  17. import java.io.File;
  18. import java.io.FileNotFoundException;
  19. import java.io.FileOutputStream;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.io.OutputStream;
  23. import java.text.MessageFormat;
  24. import java.util.ArrayList;
  25. import java.util.List;
  26. import java.util.Set;
  27. import javax.naming.Context;
  28. import javax.naming.InitialContext;
  29. import javax.naming.NamingException;
  30. import javax.servlet.ServletContext;
  31. import javax.servlet.ServletContextEvent;
  32. import org.slf4j.Logger;
  33. import org.slf4j.LoggerFactory;
  34. import com.gitblit.Constants;
  35. import com.gitblit.FileSettings;
  36. import com.gitblit.IStoredSettings;
  37. import com.gitblit.Keys;
  38. import com.gitblit.WebXmlSettings;
  39. import com.gitblit.extensions.LifeCycleListener;
  40. import com.gitblit.guice.CoreModule;
  41. import com.gitblit.guice.WebModule;
  42. import com.gitblit.manager.IAuthenticationManager;
  43. import com.gitblit.manager.IFederationManager;
  44. import com.gitblit.manager.IFilestoreManager;
  45. import com.gitblit.manager.IGitblit;
  46. import com.gitblit.manager.IManager;
  47. import com.gitblit.manager.INotificationManager;
  48. import com.gitblit.manager.IPluginManager;
  49. import com.gitblit.manager.IProjectManager;
  50. import com.gitblit.manager.IRepositoryManager;
  51. import com.gitblit.manager.IRuntimeManager;
  52. import com.gitblit.manager.IServicesManager;
  53. import com.gitblit.manager.IUserManager;
  54. import com.gitblit.tickets.ITicketService;
  55. import com.gitblit.transport.ssh.IPublicKeyManager;
  56. import com.gitblit.utils.ContainerUtils;
  57. import com.gitblit.utils.StringUtils;
  58. import com.google.inject.AbstractModule;
  59. import com.google.inject.Guice;
  60. import com.google.inject.Injector;
  61. import com.google.inject.servlet.GuiceServletContextListener;
  62. /**
  63. * This class is the main entry point for the entire webapp. It is a singleton
  64. * created manually by Gitblit GO or dynamically by the WAR/Express servlet
  65. * container. This class instantiates and starts all managers.
  66. *
  67. * Servlets and filters are injected which allows Gitblit to be completely
  68. * code-driven.
  69. *
  70. * @author James Moger
  71. *
  72. */
  73. public class GitblitContext extends GuiceServletContextListener {
  74. private static GitblitContext gitblit;
  75. protected final Logger logger = LoggerFactory.getLogger(getClass());
  76. private final List<IManager> managers = new ArrayList<IManager>();
  77. private final IStoredSettings goSettings;
  78. private final File goBaseFolder;
  79. /**
  80. * Construct a Gitblit WAR/Express context.
  81. */
  82. public GitblitContext() {
  83. this(null, null);
  84. }
  85. /**
  86. * Construct a Gitblit GO context.
  87. *
  88. * @param settings
  89. * @param baseFolder
  90. */
  91. public GitblitContext(IStoredSettings settings, File baseFolder) {
  92. this.goSettings = settings;
  93. this.goBaseFolder = baseFolder;
  94. gitblit = this;
  95. }
  96. /**
  97. * This method is only used for unit and integration testing.
  98. *
  99. * @param managerClass
  100. * @return a manager
  101. */
  102. @SuppressWarnings("unchecked")
  103. public static <X extends IManager> X getManager(Class<X> managerClass) {
  104. for (IManager manager : gitblit.managers) {
  105. if (managerClass.isAssignableFrom(manager.getClass())) {
  106. return (X) manager;
  107. }
  108. }
  109. return null;
  110. }
  111. @Override
  112. protected Injector getInjector() {
  113. return Guice.createInjector(getModules());
  114. }
  115. /**
  116. * Returns Gitblit's Guice injection modules.
  117. */
  118. protected AbstractModule [] getModules() {
  119. return new AbstractModule [] { new CoreModule(), new WebModule() };
  120. }
  121. /**
  122. * Configure Gitblit from the web.xml, if no configuration has already been
  123. * specified.
  124. *
  125. * @see ServletContextListener.contextInitialize(ServletContextEvent)
  126. */
  127. @Override
  128. public final void contextInitialized(ServletContextEvent contextEvent) {
  129. super.contextInitialized(contextEvent);
  130. ServletContext context = contextEvent.getServletContext();
  131. startCore(context);
  132. }
  133. /**
  134. * Prepare runtime settings and start all manager instances.
  135. */
  136. protected void startCore(ServletContext context) {
  137. Injector injector = (Injector) context.getAttribute(Injector.class.getName());
  138. // create the runtime settings object
  139. IStoredSettings runtimeSettings = injector.getInstance(IStoredSettings.class);
  140. final File baseFolder;
  141. if (goSettings != null) {
  142. // Gitblit GO
  143. baseFolder = configureGO(context, goSettings, goBaseFolder, runtimeSettings);
  144. } else {
  145. // servlet container
  146. WebXmlSettings webxmlSettings = new WebXmlSettings(context);
  147. String contextRealPath = context.getRealPath("/");
  148. File contextFolder = (contextRealPath != null) ? new File(contextRealPath) : null;
  149. // if the base folder dosen't match the default assume they don't want to use express,
  150. // this allows for other containers to customise the basefolder per context.
  151. String defaultBase = Constants.contextFolder$ + "/WEB-INF/data";
  152. String base = getBaseFolderPath(defaultBase);
  153. if (!StringUtils.isEmpty(System.getenv("OPENSHIFT_DATA_DIR")) && defaultBase.equals(base)) {
  154. // RedHat OpenShift
  155. baseFolder = configureExpress(context, webxmlSettings, contextFolder, runtimeSettings);
  156. } else {
  157. // standard WAR
  158. baseFolder = configureWAR(context, webxmlSettings, contextFolder, runtimeSettings);
  159. }
  160. // Test for Tomcat forward-slash/%2F issue and auto-adjust settings
  161. ContainerUtils.CVE_2007_0450.test(runtimeSettings);
  162. }
  163. // Manually configure IRuntimeManager
  164. logManager(IRuntimeManager.class);
  165. IRuntimeManager runtime = injector.getInstance(IRuntimeManager.class);
  166. runtime.setBaseFolder(baseFolder);
  167. runtime.getStatus().isGO = goSettings != null;
  168. runtime.getStatus().servletContainer = context.getServerInfo();
  169. runtime.start();
  170. managers.add(runtime);
  171. // create the plugin manager instance but do not start it
  172. loadManager(injector, IPluginManager.class);
  173. // start all other managers
  174. startManager(injector, INotificationManager.class);
  175. startManager(injector, IUserManager.class);
  176. startManager(injector, IAuthenticationManager.class);
  177. startManager(injector, IPublicKeyManager.class);
  178. startManager(injector, IRepositoryManager.class);
  179. startManager(injector, IProjectManager.class);
  180. startManager(injector, IFederationManager.class);
  181. startManager(injector, ITicketService.class);
  182. startManager(injector, IGitblit.class);
  183. startManager(injector, IServicesManager.class);
  184. startManager(injector, IFilestoreManager.class);
  185. // start the plugin manager last so that plugins can depend on
  186. // deterministic access to all other managers in their start() methods
  187. startManager(injector, IPluginManager.class);
  188. logger.info("");
  189. logger.info("All managers started.");
  190. logger.info("");
  191. IPluginManager pluginManager = injector.getInstance(IPluginManager.class);
  192. for (LifeCycleListener listener : pluginManager.getExtensions(LifeCycleListener.class)) {
  193. try {
  194. listener.onStartup();
  195. } catch (Throwable t) {
  196. logger.error(null, t);
  197. }
  198. }
  199. }
  200. private String lookupBaseFolderFromJndi() {
  201. try {
  202. // try to lookup JNDI env-entry for the baseFolder
  203. InitialContext ic = new InitialContext();
  204. Context env = (Context) ic.lookup("java:comp/env");
  205. return (String) env.lookup("baseFolder");
  206. } catch (NamingException n) {
  207. logger.error("Failed to get JNDI env-entry: " + n.getExplanation());
  208. }
  209. return null;
  210. }
  211. protected String getBaseFolderPath(String defaultBaseFolder) {
  212. // try a system property or a JNDI property
  213. String specifiedBaseFolder = System.getProperty("GITBLIT_HOME", lookupBaseFolderFromJndi());
  214. if (!StringUtils.isEmpty(System.getenv("GITBLIT_HOME"))) {
  215. // try an environment variable
  216. specifiedBaseFolder = System.getenv("GITBLIT_HOME");
  217. }
  218. if (!StringUtils.isEmpty(specifiedBaseFolder)) {
  219. // use specified base folder path
  220. return specifiedBaseFolder;
  221. }
  222. // use default base folder path
  223. return defaultBaseFolder;
  224. }
  225. protected <X extends IManager> X loadManager(Injector injector, Class<X> clazz) {
  226. X x = injector.getInstance(clazz);
  227. return x;
  228. }
  229. protected <X extends IManager> X startManager(Injector injector, Class<X> clazz) {
  230. X x = loadManager(injector, clazz);
  231. logManager(clazz);
  232. return startManager(x);
  233. }
  234. protected <X extends IManager> X startManager(X x) {
  235. x.start();
  236. managers.add(x);
  237. return x;
  238. }
  239. protected void logManager(Class<? extends IManager> clazz) {
  240. logger.info("");
  241. logger.info("----[{}]----", clazz.getName());
  242. }
  243. @Override
  244. public final void contextDestroyed(ServletContextEvent contextEvent) {
  245. super.contextDestroyed(contextEvent);
  246. ServletContext context = contextEvent.getServletContext();
  247. destroyContext(context);
  248. }
  249. /**
  250. * Gitblit is being shutdown either because the servlet container is
  251. * shutting down or because the servlet container is re-deploying Gitblit.
  252. */
  253. protected void destroyContext(ServletContext context) {
  254. logger.info("Gitblit context destroyed by servlet container.");
  255. IPluginManager pluginManager = getManager(IPluginManager.class);
  256. if (pluginManager != null) {
  257. for (LifeCycleListener listener : pluginManager.getExtensions(LifeCycleListener.class)) {
  258. try {
  259. listener.onShutdown();
  260. } catch (Throwable t) {
  261. logger.error(null, t);
  262. }
  263. }
  264. }
  265. for (IManager manager : managers) {
  266. logger.debug("stopping {}", manager.getClass().getSimpleName());
  267. manager.stop();
  268. }
  269. }
  270. /**
  271. * Configures Gitblit GO
  272. *
  273. * @param context
  274. * @param settings
  275. * @param baseFolder
  276. * @param runtimeSettings
  277. * @return the base folder
  278. */
  279. protected File configureGO(
  280. ServletContext context,
  281. IStoredSettings goSettings,
  282. File goBaseFolder,
  283. IStoredSettings runtimeSettings) {
  284. logger.debug("configuring Gitblit GO");
  285. // merge the stored settings into the runtime settings
  286. //
  287. // if runtimeSettings is also a FileSettings w/o a specified target file,
  288. // the target file for runtimeSettings is set to "localSettings".
  289. runtimeSettings.merge(goSettings);
  290. File base = goBaseFolder;
  291. return base;
  292. }
  293. /**
  294. * Configures a standard WAR instance of Gitblit.
  295. *
  296. * @param context
  297. * @param webxmlSettings
  298. * @param contextFolder
  299. * @param runtimeSettings
  300. * @return the base folder
  301. */
  302. protected File configureWAR(
  303. ServletContext context,
  304. WebXmlSettings webxmlSettings,
  305. File contextFolder,
  306. IStoredSettings runtimeSettings) {
  307. // Gitblit is running in a standard servlet container
  308. logger.debug("configuring Gitblit WAR");
  309. logger.info("WAR contextFolder is " + ((contextFolder != null) ? contextFolder.getAbsolutePath() : "<empty>"));
  310. String webXmlPath = webxmlSettings.getString(Constants.baseFolder, Constants.contextFolder$ + "/WEB-INF/data");
  311. if (webXmlPath.contains(Constants.contextFolder$) && contextFolder == null) {
  312. // warn about null contextFolder (issue-199)
  313. logger.error("");
  314. logger.error(MessageFormat.format("\"{0}\" depends on \"{1}\" but \"{2}\" is returning NULL for \"{1}\"!",
  315. Constants.baseFolder, Constants.contextFolder$, context.getServerInfo()));
  316. logger.error(MessageFormat.format("Please specify a non-parameterized path for <context-param> {0} in web.xml!!", Constants.baseFolder));
  317. logger.error(MessageFormat.format("OR configure your servlet container to specify a \"{0}\" parameter in the context configuration!!", Constants.baseFolder));
  318. logger.error("");
  319. }
  320. String baseFolderPath = getBaseFolderPath(webXmlPath);
  321. File baseFolder = com.gitblit.utils.FileUtils.resolveParameter(Constants.contextFolder$, contextFolder, baseFolderPath);
  322. baseFolder.mkdirs();
  323. // try to extract the data folder resource to the baseFolder
  324. extractResources(context, "/WEB-INF/data/", baseFolder);
  325. // delegate all config to baseFolder/gitblit.properties file
  326. File localSettings = new File(baseFolder, "gitblit.properties");
  327. FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath());
  328. // merge the stored settings into the runtime settings
  329. //
  330. // if runtimeSettings is also a FileSettings w/o a specified target file,
  331. // the target file for runtimeSettings is set to "localSettings".
  332. runtimeSettings.merge(fileSettings);
  333. return baseFolder;
  334. }
  335. /**
  336. * Configures an OpenShift instance of Gitblit.
  337. *
  338. * @param context
  339. * @param webxmlSettings
  340. * @param contextFolder
  341. * @param runtimeSettings
  342. * @return the base folder
  343. */
  344. private File configureExpress(
  345. ServletContext context,
  346. WebXmlSettings webxmlSettings,
  347. File contextFolder,
  348. IStoredSettings runtimeSettings) {
  349. // Gitblit is running in OpenShift/JBoss
  350. logger.debug("configuring Gitblit Express");
  351. String openShift = System.getenv("OPENSHIFT_DATA_DIR");
  352. File base = new File(openShift);
  353. logger.info("EXPRESS contextFolder is " + contextFolder.getAbsolutePath());
  354. // Copy the included scripts to the configured groovy folder
  355. String path = webxmlSettings.getString(Keys.groovy.scriptsFolder, "groovy");
  356. File localScripts = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, path);
  357. if (!localScripts.exists()) {
  358. File warScripts = new File(contextFolder, "/WEB-INF/data/groovy");
  359. if (!warScripts.equals(localScripts)) {
  360. try {
  361. com.gitblit.utils.FileUtils.copy(localScripts, warScripts.listFiles());
  362. } catch (IOException e) {
  363. logger.error(MessageFormat.format(
  364. "Failed to copy included Groovy scripts from {0} to {1}",
  365. warScripts, localScripts));
  366. }
  367. }
  368. }
  369. // Copy the included gitignore files to the configured gitignore folder
  370. String gitignorePath = webxmlSettings.getString(Keys.git.gitignoreFolder, "gitignore");
  371. File localGitignores = com.gitblit.utils.FileUtils.resolveParameter(Constants.baseFolder$, base, gitignorePath);
  372. if (!localGitignores.exists()) {
  373. File warGitignores = new File(contextFolder, "/WEB-INF/data/gitignore");
  374. if (!warGitignores.equals(localGitignores)) {
  375. try {
  376. com.gitblit.utils.FileUtils.copy(localGitignores, warGitignores.listFiles());
  377. } catch (IOException e) {
  378. logger.error(MessageFormat.format(
  379. "Failed to copy included .gitignore files from {0} to {1}",
  380. warGitignores, localGitignores));
  381. }
  382. }
  383. }
  384. // merge the WebXmlSettings into the runtime settings (for backwards-compatibilty)
  385. runtimeSettings.merge(webxmlSettings);
  386. // settings are to be stored in openshift/gitblit.properties
  387. File localSettings = new File(base, "gitblit.properties");
  388. FileSettings fileSettings = new FileSettings(localSettings.getAbsolutePath());
  389. // merge the stored settings into the runtime settings
  390. //
  391. // if runtimeSettings is also a FileSettings w/o a specified target file,
  392. // the target file for runtimeSettings is set to "localSettings".
  393. runtimeSettings.merge(fileSettings);
  394. return base;
  395. }
  396. protected void extractResources(ServletContext context, String path, File toDir) {
  397. Set<String> resources = context.getResourcePaths(path);
  398. if (resources == null) {
  399. logger.warn("There are no WAR resources to extract from {}", path);
  400. return;
  401. }
  402. for (String resource : resources) {
  403. // extract the resource to the directory if it does not exist
  404. File f = new File(toDir, resource.substring(path.length()));
  405. if (!f.exists()) {
  406. InputStream is = null;
  407. OutputStream os = null;
  408. try {
  409. if (resource.charAt(resource.length() - 1) == '/') {
  410. // directory
  411. f.mkdirs();
  412. extractResources(context, resource, f);
  413. } else {
  414. // file
  415. f.getParentFile().mkdirs();
  416. is = context.getResourceAsStream(resource);
  417. os = new FileOutputStream(f);
  418. byte [] buffer = new byte[4096];
  419. int len = 0;
  420. while ((len = is.read(buffer)) > -1) {
  421. os.write(buffer, 0, len);
  422. }
  423. }
  424. } catch (FileNotFoundException e) {
  425. logger.error("Failed to find resource \"" + resource + "\"", e);
  426. } catch (IOException e) {
  427. logger.error("Failed to copy resource \"" + resource + "\" to " + f, e);
  428. } finally {
  429. if (is != null) {
  430. try {
  431. is.close();
  432. } catch (IOException e) {
  433. // ignore
  434. }
  435. }
  436. if (os != null) {
  437. try {
  438. os.close();
  439. } catch (IOException e) {
  440. // ignore
  441. }
  442. }
  443. }
  444. }
  445. }
  446. }
  447. }